mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-21 21:33:07 +00:00
8304265: Implementation of Foreign Function and Memory API (Third Preview)
Co-authored-by: Maurizio Cimadamore <mcimadamore@openjdk.org> Co-authored-by: Jorn Vernee <jvernee@openjdk.org> Co-authored-by: Paul Sandoz <psandoz@openjdk.org> Co-authored-by: Feilong Jiang <fjiang@openjdk.org> Co-authored-by: Per Minborg <pminborg@openjdk.org> Reviewed-by: erikj, jvernee, vlivanov, psandoz
This commit is contained in:
parent
41d58533ac
commit
cbccc4c817
@ -223,6 +223,10 @@ JDKOPT_SETUP_UNDEFINED_BEHAVIOR_SANITIZER
|
||||
# LeakSanitizer
|
||||
JDKOPT_SETUP_LEAK_SANITIZER
|
||||
|
||||
# Fallback linker
|
||||
# This needs to go before 'LIB_DETERMINE_DEPENDENCIES'
|
||||
JDKOPT_SETUP_FALLBACK_LINKER
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Check dependencies for external and internal libraries.
|
||||
|
||||
@ -903,3 +903,22 @@ AC_DEFUN([JDKOPT_SETUP_MACOSX_SIGNING],
|
||||
AC_SUBST(MACOSX_CODESIGN_MODE)
|
||||
fi
|
||||
])
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# fallback linker
|
||||
#
|
||||
AC_DEFUN_ONCE([JDKOPT_SETUP_FALLBACK_LINKER],
|
||||
[
|
||||
FALLBACK_LINKER_DEFAULT=false
|
||||
|
||||
if HOTSPOT_CHECK_JVM_VARIANT(zero); then
|
||||
FALLBACK_LINKER_DEFAULT=true
|
||||
fi
|
||||
|
||||
UTIL_ARG_ENABLE(NAME: fallback-linker, DEFAULT: $FALLBACK_LINKER_DEFAULT,
|
||||
RESULT: ENABLE_FALLBACK_LINKER,
|
||||
DESC: [enable libffi-based fallback implementation of java.lang.foreign.Linker],
|
||||
CHECKING_MSG: [if fallback linker enabled])
|
||||
AC_SUBST(ENABLE_FALLBACK_LINKER)
|
||||
])
|
||||
|
||||
@ -82,7 +82,7 @@ AC_DEFUN_ONCE([LIB_DETERMINE_DEPENDENCIES],
|
||||
fi
|
||||
|
||||
# Check if ffi is needed
|
||||
if HOTSPOT_CHECK_JVM_VARIANT(zero); then
|
||||
if HOTSPOT_CHECK_JVM_VARIANT(zero) || test "x$ENABLE_FALLBACK_LINKER" = "xtrue"; then
|
||||
NEEDS_LIB_FFI=true
|
||||
else
|
||||
NEEDS_LIB_FFI=false
|
||||
|
||||
@ -409,6 +409,9 @@ TEST_JOBS?=@TEST_JOBS@
|
||||
DEFAULT_MAKE_TARGET:=@DEFAULT_MAKE_TARGET@
|
||||
DEFAULT_LOG:=@DEFAULT_LOG@
|
||||
|
||||
# Fallback linker
|
||||
ENABLE_FALLBACK_LINKER:=@ENABLE_FALLBACK_LINKER@
|
||||
|
||||
FREETYPE_TO_USE:=@FREETYPE_TO_USE@
|
||||
FREETYPE_LIBS:=@FREETYPE_LIBS@
|
||||
FREETYPE_CFLAGS:=@FREETYPE_CFLAGS@
|
||||
|
||||
@ -587,11 +587,12 @@ var getJibProfilesProfiles = function (input, common, data) {
|
||||
"linux-x64-zero": {
|
||||
target_os: "linux",
|
||||
target_cpu: "x64",
|
||||
dependencies: ["devkit", "gtest"],
|
||||
dependencies: ["devkit", "gtest", "libffi"],
|
||||
configure_args: concat(common.configure_args_64bit, [
|
||||
"--with-zlib=system",
|
||||
"--with-jvm-variants=zero",
|
||||
"--enable-libffi-bundling"
|
||||
"--with-libffi=" + input.get("libffi", "home_path"),
|
||||
"--enable-libffi-bundling",
|
||||
])
|
||||
},
|
||||
|
||||
@ -744,6 +745,40 @@ var getJibProfilesProfiles = function (input, common, data) {
|
||||
common.debug_profile_artifacts(artifactData[name]));
|
||||
});
|
||||
|
||||
// Define artifact just for linux-x64-zero, which is the only one we test on
|
||||
["linux-x64"].forEach(function (name) {
|
||||
var o = artifactData[name]
|
||||
var pf = o.platform
|
||||
var jdk_subdir = (o.jdk_subdir != null ? o.jdk_subdir : "jdk-" + data.version);
|
||||
var jdk_suffix = (o.jdk_suffix != null ? o.jdk_suffix : "tar.gz");
|
||||
var zeroName = name + "-zero";
|
||||
profiles[zeroName].artifacts = {
|
||||
jdk: {
|
||||
local: "bundles/\\(jdk.*bin." + jdk_suffix + "\\)",
|
||||
remote: [
|
||||
"bundles/" + pf + "/jdk-" + data.version + "_" + pf + "_bin-zero." + jdk_suffix,
|
||||
],
|
||||
subdir: jdk_subdir,
|
||||
exploded: "images/jdk",
|
||||
},
|
||||
test: {
|
||||
local: "bundles/\\(jdk.*bin-tests.tar.gz\\)",
|
||||
remote: [
|
||||
"bundles/" + pf + "/jdk-" + data.version + "_" + pf + "_bin-zero-tests.tar.gz",
|
||||
],
|
||||
exploded: "images/test"
|
||||
},
|
||||
jdk_symbols: {
|
||||
local: "bundles/\\(jdk.*bin-symbols.tar.gz\\)",
|
||||
remote: [
|
||||
"bundles/" + pf + "/jdk-" + data.version + "_" + pf + "_bin-zero-symbols.tar.gz",
|
||||
],
|
||||
subdir: jdk_subdir,
|
||||
exploded: "images/jdk"
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
buildJdkDep = input.build_os + "-" + input.build_cpu + ".jdk";
|
||||
docsProfiles = {
|
||||
"docs": {
|
||||
@ -1234,6 +1269,13 @@ var getJibProfilesDependencies = function (input, common) {
|
||||
ext: "tar.gz",
|
||||
revision: "1.13.0+1.0"
|
||||
},
|
||||
|
||||
libffi: {
|
||||
organization: common.organization,
|
||||
module: "libffi-" + input.build_platform,
|
||||
ext: "tar.gz",
|
||||
revision: "3.4.2+1.0"
|
||||
},
|
||||
};
|
||||
|
||||
return dependencies;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2016, 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
|
||||
@ -30,5 +30,6 @@ jio_vsnprintf
|
||||
JNI_CreateJavaVM
|
||||
JNI_GetCreatedJavaVMs
|
||||
JNI_GetDefaultJavaVMInitArgs
|
||||
JVM_IsForeignLinkerSupported
|
||||
JVM_FindClassFromBootLoader
|
||||
JVM_InitAgentProperties
|
||||
|
||||
111
make/devkit/createLibffiBundle.sh
Normal file
111
make/devkit/createLibffiBundle.sh
Normal file
@ -0,0 +1,111 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
# This script generates a libffi bundle. On linux by building it from source
|
||||
# using a devkit, which should match the devkit used to build the JDK.
|
||||
#
|
||||
# Set MAKE_ARGS to add parameters to make. Ex:
|
||||
#
|
||||
# $ MAKE_ARGS=-j32 bash createLibffiBundle.sh
|
||||
#
|
||||
# The script tries to behave well on multiple invocations, only performing steps
|
||||
# not already done. To redo a step, manually delete the target files from that
|
||||
# step.
|
||||
#
|
||||
# Note that the libtool and texinfo packages are needed to build libffi
|
||||
# $ sudo apt install libtool texinfo
|
||||
|
||||
LIBFFI_VERSION=3.4.2
|
||||
|
||||
BUNDLE_NAME=libffi-$LIBFFI_VERSION.tar.gz
|
||||
|
||||
SCRIPT_FILE="$(basename $0)"
|
||||
SCRIPT_DIR="$(cd "$(dirname $0)" > /dev/null && pwd)"
|
||||
OUTPUT_DIR="${SCRIPT_DIR}/../../build/libffi"
|
||||
SRC_DIR="$OUTPUT_DIR/src"
|
||||
DOWNLOAD_DIR="$OUTPUT_DIR/download"
|
||||
INSTALL_DIR="$OUTPUT_DIR/install"
|
||||
IMAGE_DIR="$OUTPUT_DIR/image"
|
||||
|
||||
USAGE="$0 <devkit dir>"
|
||||
|
||||
if [ "$1" = "" ]; then
|
||||
echo $USAGE
|
||||
exit 1
|
||||
fi
|
||||
DEVKIT_DIR="$1"
|
||||
|
||||
# Download source distros
|
||||
mkdir -p $DOWNLOAD_DIR
|
||||
cd $DOWNLOAD_DIR
|
||||
SOURCE_TAR=v$LIBFFI_VERSION.tar.gz
|
||||
if [ ! -f $SOURCE_TAR ]; then
|
||||
wget https://github.com/libffi/libffi/archive/refs/tags/v$LIBFFI_VERSION.tar.gz
|
||||
fi
|
||||
|
||||
# Unpack src
|
||||
mkdir -p $SRC_DIR
|
||||
cd $SRC_DIR
|
||||
LIBFFI_DIRNAME=libffi-$LIBFFI_VERSION
|
||||
LIBFFI_DIR=$SRC_DIR/$LIBFFI_DIRNAME
|
||||
if [ ! -d $LIBFFI_DIRNAME ]; then
|
||||
echo "Unpacking $SOURCE_TAR"
|
||||
tar xf $DOWNLOAD_DIR/$SOURCE_TAR
|
||||
fi
|
||||
|
||||
# Build
|
||||
cd $LIBFFI_DIR
|
||||
if [ ! -e $LIBFFI_DIR/configure ]; then
|
||||
bash ./autogen.sh
|
||||
fi
|
||||
bash ./configure --prefix=$INSTALL_DIR CC=$DEVKIT_DIR/bin/gcc CXX=$DEVKIT_DIR/bin/g++
|
||||
|
||||
# Run with nice to keep system usable during build.
|
||||
nice make $MAKE_ARGS install
|
||||
|
||||
mkdir -p $IMAGE_DIR
|
||||
# Extract what we need into an image
|
||||
if [ ! -e $IMAGE_DIR/lib/libffi.so ]; then
|
||||
echo "Copying libffi.so* to image"
|
||||
mkdir -p $IMAGE_DIR/lib
|
||||
cp -a $INSTALL_DIR/lib64/libffi.so* $IMAGE_DIR/lib/
|
||||
fi
|
||||
if [ ! -e $IMAGE_DIR/include/ ]; then
|
||||
echo "Copying include to image"
|
||||
mkdir -p $IMAGE_DIR/include
|
||||
cp -a $INSTALL_DIR/include/. $IMAGE_DIR/include/
|
||||
fi
|
||||
if [ ! -e $IMAGE_DIR/$SCRIPT_FILE ]; then
|
||||
echo "Copying this script to image"
|
||||
cp -a $SCRIPT_DIR/$SCRIPT_FILE $IMAGE_DIR/
|
||||
fi
|
||||
|
||||
# Create bundle
|
||||
if [ ! -e $OUTPUT_DIR/$BUNDLE_NAME ]; then
|
||||
echo "Creating $OUTPUT_DIR/$BUNDLE_NAME"
|
||||
cd $IMAGE_DIR
|
||||
tar zcf $OUTPUT_DIR/$BUNDLE_NAME *
|
||||
fi
|
||||
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2011, 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
|
||||
@ -215,3 +215,18 @@ $(eval $(call SetupJdkLibrary, BUILD_SYSLOOKUPLIB, \
|
||||
))
|
||||
|
||||
TARGETS += $(BUILD_SYSLOOKUPLIB)
|
||||
|
||||
################################################################################
|
||||
# Create fallback linker lib
|
||||
|
||||
ifeq ($(ENABLE_FALLBACK_LINKER), true)
|
||||
$(eval $(call SetupJdkLibrary, BUILD_LIBFALLBACKLINKER, \
|
||||
NAME := fallbackLinker, \
|
||||
CFLAGS := $(CFLAGS_JDKLIB) $(LIBFFI_CFLAGS), \
|
||||
LDFLAGS := $(LDFLAGS_JDKLIB) \
|
||||
$(call SET_SHARED_LIBRARY_ORIGIN), \
|
||||
LIBS := $(LIBFFI_LIBS), \
|
||||
))
|
||||
|
||||
TARGETS += $(BUILD_LIBFALLBACKLINKER)
|
||||
endif
|
||||
|
||||
@ -47,6 +47,7 @@ class DowncallStubGenerator : public StubCodeGenerator {
|
||||
|
||||
bool _needs_return_buffer;
|
||||
int _captured_state_mask;
|
||||
bool _needs_transition;
|
||||
|
||||
int _frame_complete;
|
||||
int _frame_size_slots;
|
||||
@ -60,7 +61,8 @@ public:
|
||||
const GrowableArray<VMStorage>& input_registers,
|
||||
const GrowableArray<VMStorage>& output_registers,
|
||||
bool needs_return_buffer,
|
||||
int captured_state_mask)
|
||||
int captured_state_mask,
|
||||
bool needs_transition)
|
||||
: StubCodeGenerator(buffer, PrintMethodHandleStubs),
|
||||
_signature(signature),
|
||||
_num_args(num_args),
|
||||
@ -70,6 +72,7 @@ public:
|
||||
_output_registers(output_registers),
|
||||
_needs_return_buffer(needs_return_buffer),
|
||||
_captured_state_mask(captured_state_mask),
|
||||
_needs_transition(needs_transition),
|
||||
_frame_complete(0),
|
||||
_frame_size_slots(0),
|
||||
_oop_maps(NULL) {
|
||||
@ -100,13 +103,15 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature,
|
||||
const GrowableArray<VMStorage>& input_registers,
|
||||
const GrowableArray<VMStorage>& output_registers,
|
||||
bool needs_return_buffer,
|
||||
int captured_state_mask) {
|
||||
int captured_state_mask,
|
||||
bool needs_transition) {
|
||||
int code_size = native_invoker_code_base_size + (num_args * native_invoker_size_per_arg);
|
||||
int locs_size = 1; // must be non-zero
|
||||
CodeBuffer code("nep_invoker_blob", code_size, locs_size);
|
||||
DowncallStubGenerator g(&code, signature, num_args, ret_bt, abi,
|
||||
input_registers, output_registers,
|
||||
needs_return_buffer, captured_state_mask);
|
||||
needs_return_buffer, captured_state_mask,
|
||||
needs_transition);
|
||||
g.generate();
|
||||
code.log_section_sizes("nep_invoker_blob");
|
||||
|
||||
@ -163,7 +168,7 @@ void DowncallStubGenerator::generate() {
|
||||
assert(_abi._shadow_space_bytes == 0, "not expecting shadow space on AArch64");
|
||||
allocated_frame_size += arg_shuffle.out_arg_bytes();
|
||||
|
||||
bool should_save_return_value = !_needs_return_buffer;
|
||||
bool should_save_return_value = !_needs_return_buffer && _needs_transition;
|
||||
RegSpiller out_reg_spiller(_output_registers);
|
||||
int spill_offset = -1;
|
||||
|
||||
@ -191,7 +196,7 @@ void DowncallStubGenerator::generate() {
|
||||
_frame_size_slots = align_up(framesize + (allocated_frame_size >> LogBytesPerInt), 4);
|
||||
assert(is_even(_frame_size_slots/2), "sp not 16-byte aligned");
|
||||
|
||||
_oop_maps = new OopMapSet();
|
||||
_oop_maps = _needs_transition ? new OopMapSet() : nullptr;
|
||||
address start = __ pc();
|
||||
|
||||
__ enter();
|
||||
@ -201,15 +206,17 @@ void DowncallStubGenerator::generate() {
|
||||
|
||||
_frame_complete = __ pc() - start;
|
||||
|
||||
address the_pc = __ pc();
|
||||
__ set_last_Java_frame(sp, rfp, the_pc, tmp1);
|
||||
OopMap* map = new OopMap(_frame_size_slots, 0);
|
||||
_oop_maps->add_gc_map(the_pc - start, map);
|
||||
if (_needs_transition) {
|
||||
address the_pc = __ pc();
|
||||
__ set_last_Java_frame(sp, rfp, the_pc, tmp1);
|
||||
OopMap* map = new OopMap(_frame_size_slots, 0);
|
||||
_oop_maps->add_gc_map(the_pc - start, map);
|
||||
|
||||
// State transition
|
||||
__ mov(tmp1, _thread_in_native);
|
||||
__ lea(tmp2, Address(rthread, JavaThread::thread_state_offset()));
|
||||
__ stlrw(tmp1, tmp2);
|
||||
// State transition
|
||||
__ mov(tmp1, _thread_in_native);
|
||||
__ lea(tmp2, Address(rthread, JavaThread::thread_state_offset()));
|
||||
__ stlrw(tmp1, tmp2);
|
||||
}
|
||||
|
||||
__ block_comment("{ argument shuffle");
|
||||
arg_shuffle.generate(_masm, shuffle_reg, 0, _abi._shadow_space_bytes, locs);
|
||||
@ -257,86 +264,89 @@ void DowncallStubGenerator::generate() {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
__ mov(tmp1, _thread_in_native_trans);
|
||||
__ strw(tmp1, Address(rthread, JavaThread::thread_state_offset()));
|
||||
|
||||
// Force this write out before the read below
|
||||
if (!UseSystemMemoryBarrier) {
|
||||
__ membar(Assembler::LoadLoad | Assembler::LoadStore |
|
||||
Assembler::StoreLoad | Assembler::StoreStore);
|
||||
}
|
||||
|
||||
__ verify_sve_vector_length(tmp1);
|
||||
|
||||
Label L_after_safepoint_poll;
|
||||
Label L_safepoint_poll_slow_path;
|
||||
|
||||
__ safepoint_poll(L_safepoint_poll_slow_path, true /* at_return */, true /* acquire */, false /* in_nmethod */, tmp1);
|
||||
|
||||
__ ldrw(tmp1, Address(rthread, JavaThread::suspend_flags_offset()));
|
||||
__ cbnzw(tmp1, L_safepoint_poll_slow_path);
|
||||
|
||||
__ bind(L_after_safepoint_poll);
|
||||
|
||||
// change thread state
|
||||
__ mov(tmp1, _thread_in_Java);
|
||||
__ lea(tmp2, Address(rthread, JavaThread::thread_state_offset()));
|
||||
__ stlrw(tmp1, tmp2);
|
||||
|
||||
__ block_comment("reguard stack check");
|
||||
Label L_reguard;
|
||||
Label L_after_reguard;
|
||||
__ ldrb(tmp1, Address(rthread, JavaThread::stack_guard_state_offset()));
|
||||
__ cmpw(tmp1, StackOverflow::stack_guard_yellow_reserved_disabled);
|
||||
__ br(Assembler::EQ, L_reguard);
|
||||
__ bind(L_after_reguard);
|
||||
if (_needs_transition) {
|
||||
__ mov(tmp1, _thread_in_native_trans);
|
||||
__ strw(tmp1, Address(rthread, JavaThread::thread_state_offset()));
|
||||
|
||||
__ reset_last_Java_frame(true);
|
||||
// Force this write out before the read below
|
||||
if (!UseSystemMemoryBarrier) {
|
||||
__ membar(Assembler::LoadLoad | Assembler::LoadStore |
|
||||
Assembler::StoreLoad | Assembler::StoreStore);
|
||||
}
|
||||
|
||||
__ verify_sve_vector_length(tmp1);
|
||||
|
||||
__ safepoint_poll(L_safepoint_poll_slow_path, true /* at_return */, true /* acquire */, false /* in_nmethod */, tmp1);
|
||||
|
||||
__ ldrw(tmp1, Address(rthread, JavaThread::suspend_flags_offset()));
|
||||
__ cbnzw(tmp1, L_safepoint_poll_slow_path);
|
||||
|
||||
__ bind(L_after_safepoint_poll);
|
||||
|
||||
// change thread state
|
||||
__ mov(tmp1, _thread_in_Java);
|
||||
__ lea(tmp2, Address(rthread, JavaThread::thread_state_offset()));
|
||||
__ stlrw(tmp1, tmp2);
|
||||
|
||||
__ block_comment("reguard stack check");
|
||||
__ ldrb(tmp1, Address(rthread, JavaThread::stack_guard_state_offset()));
|
||||
__ cmpw(tmp1, StackOverflow::stack_guard_yellow_reserved_disabled);
|
||||
__ br(Assembler::EQ, L_reguard);
|
||||
__ bind(L_after_reguard);
|
||||
|
||||
__ reset_last_Java_frame(true);
|
||||
}
|
||||
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(lr);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
__ block_comment("{ L_safepoint_poll_slow_path");
|
||||
__ bind(L_safepoint_poll_slow_path);
|
||||
if (_needs_transition) {
|
||||
__ block_comment("{ L_safepoint_poll_slow_path");
|
||||
__ bind(L_safepoint_poll_slow_path);
|
||||
|
||||
if (should_save_return_value) {
|
||||
// Need to save the native result registers around any runtime calls.
|
||||
out_reg_spiller.generate_spill(_masm, spill_offset);
|
||||
}
|
||||
if (should_save_return_value) {
|
||||
// Need to save the native result registers around any runtime calls.
|
||||
out_reg_spiller.generate_spill(_masm, spill_offset);
|
||||
}
|
||||
|
||||
__ mov(c_rarg0, rthread);
|
||||
assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area");
|
||||
__ lea(tmp1, RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans)));
|
||||
__ blr(tmp1);
|
||||
__ mov(c_rarg0, rthread);
|
||||
assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area");
|
||||
__ lea(tmp1, RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans)));
|
||||
__ blr(tmp1);
|
||||
|
||||
if (should_save_return_value) {
|
||||
out_reg_spiller.generate_fill(_masm, spill_offset);
|
||||
}
|
||||
if (should_save_return_value) {
|
||||
out_reg_spiller.generate_fill(_masm, spill_offset);
|
||||
}
|
||||
|
||||
__ b(L_after_safepoint_poll);
|
||||
__ block_comment("} L_safepoint_poll_slow_path");
|
||||
__ b(L_after_safepoint_poll);
|
||||
__ block_comment("} L_safepoint_poll_slow_path");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
__ block_comment("{ L_reguard");
|
||||
__ bind(L_reguard);
|
||||
__ block_comment("{ L_reguard");
|
||||
__ bind(L_reguard);
|
||||
|
||||
if (should_save_return_value) {
|
||||
out_reg_spiller.generate_spill(_masm, spill_offset);
|
||||
if (should_save_return_value) {
|
||||
out_reg_spiller.generate_spill(_masm, spill_offset);
|
||||
}
|
||||
|
||||
__ rt_call(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages), tmp1);
|
||||
|
||||
if (should_save_return_value) {
|
||||
out_reg_spiller.generate_fill(_masm, spill_offset);
|
||||
}
|
||||
|
||||
__ b(L_after_reguard);
|
||||
|
||||
__ block_comment("} L_reguard");
|
||||
}
|
||||
|
||||
__ rt_call(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages), tmp1);
|
||||
|
||||
if (should_save_return_value) {
|
||||
out_reg_spiller.generate_fill(_masm, spill_offset);
|
||||
}
|
||||
|
||||
__ b(L_after_reguard);
|
||||
|
||||
__ block_comment("} L_reguard");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
__ flush();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2022, Arm Limited. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -33,6 +33,10 @@
|
||||
#include "prims/vmstorage.hpp"
|
||||
#include "utilities/formatBuffer.hpp"
|
||||
|
||||
bool ForeignGlobals::is_foreign_linker_supported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ABIDescriptor::is_volatile_reg(Register reg) const {
|
||||
return _integer_argument_registers.contains(reg)
|
||||
|| _integer_additional_volatile_registers.contains(reg);
|
||||
|
||||
@ -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.
|
||||
* Copyright (c) 2020, Red Hat, Inc. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -33,7 +33,8 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature,
|
||||
const GrowableArray<VMStorage>& input_registers,
|
||||
const GrowableArray<VMStorage>& output_registers,
|
||||
bool needs_return_buffer,
|
||||
int captured_state_mask) {
|
||||
int captured_state_mask,
|
||||
bool needs_transition) {
|
||||
Unimplemented();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -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.
|
||||
* Copyright (c) 2020, Red Hat, Inc. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -29,6 +29,10 @@
|
||||
|
||||
class MacroAssembler;
|
||||
|
||||
bool ForeignGlobals::is_foreign_linker_supported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ABIDescriptor ForeignGlobals::parse_abi_descriptor(jobject jabi) {
|
||||
Unimplemented();
|
||||
return {};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2020 SAP SE. All rights reserved.
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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
|
||||
@ -33,7 +33,8 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature,
|
||||
const GrowableArray<VMStorage>& input_registers,
|
||||
const GrowableArray<VMStorage>& output_registers,
|
||||
bool needs_return_buffer,
|
||||
int captured_state_mask) {
|
||||
int captured_state_mask,
|
||||
bool needs_transition) {
|
||||
Unimplemented();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020 SAP SE. All rights reserved.
|
||||
* Copyright (c) 2020, 2023, SAP SE. All rights reserved.
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -29,6 +29,10 @@
|
||||
|
||||
class MacroAssembler;
|
||||
|
||||
bool ForeignGlobals::is_foreign_linker_supported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Stubbed out, implement later
|
||||
const ABIDescriptor ForeignGlobals::parse_abi_descriptor(jobject jabi) {
|
||||
Unimplemented();
|
||||
|
||||
@ -48,6 +48,7 @@ class DowncallStubGenerator : public StubCodeGenerator {
|
||||
|
||||
bool _needs_return_buffer;
|
||||
int _captured_state_mask;
|
||||
bool _needs_transition;
|
||||
|
||||
int _frame_complete;
|
||||
int _frame_size_slots;
|
||||
@ -61,7 +62,8 @@ public:
|
||||
const GrowableArray<VMStorage>& input_registers,
|
||||
const GrowableArray<VMStorage>& output_registers,
|
||||
bool needs_return_buffer,
|
||||
int captured_state_mask)
|
||||
int captured_state_mask,
|
||||
bool needs_transition)
|
||||
: StubCodeGenerator(buffer, PrintMethodHandleStubs),
|
||||
_signature(signature),
|
||||
_num_args(num_args),
|
||||
@ -71,6 +73,7 @@ public:
|
||||
_output_registers(output_registers),
|
||||
_needs_return_buffer(needs_return_buffer),
|
||||
_captured_state_mask(captured_state_mask),
|
||||
_needs_transition(needs_transition),
|
||||
_frame_complete(0),
|
||||
_frame_size_slots(0),
|
||||
_oop_maps(nullptr) {
|
||||
@ -101,13 +104,15 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature,
|
||||
const GrowableArray<VMStorage>& input_registers,
|
||||
const GrowableArray<VMStorage>& output_registers,
|
||||
bool needs_return_buffer,
|
||||
int captured_state_mask) {
|
||||
int captured_state_mask,
|
||||
bool needs_transition) {
|
||||
int code_size = native_invoker_code_base_size + (num_args * native_invoker_size_per_arg);
|
||||
int locs_size = 1; // must be non-zero
|
||||
CodeBuffer code("nep_invoker_blob", code_size, locs_size);
|
||||
DowncallStubGenerator g(&code, signature, num_args, ret_bt, abi,
|
||||
input_registers, output_registers,
|
||||
needs_return_buffer, captured_state_mask);
|
||||
needs_return_buffer, captured_state_mask,
|
||||
needs_transition);
|
||||
g.generate();
|
||||
code.log_section_sizes("nep_invoker_blob");
|
||||
|
||||
@ -160,7 +165,7 @@ void DowncallStubGenerator::generate() {
|
||||
assert(_abi._shadow_space_bytes == 0, "not expecting shadow space on RISCV64");
|
||||
allocated_frame_size += arg_shuffle.out_arg_bytes();
|
||||
|
||||
bool should_save_return_value = !_needs_return_buffer;
|
||||
bool should_save_return_value = !_needs_return_buffer && _needs_transition;
|
||||
RegSpiller out_reg_spiller(_output_registers);
|
||||
int spill_offset = -1;
|
||||
|
||||
@ -190,7 +195,7 @@ void DowncallStubGenerator::generate() {
|
||||
_frame_size_slots += framesize + (allocated_frame_size >> LogBytesPerInt);
|
||||
assert(is_even(_frame_size_slots / 2), "sp not 16-byte aligned");
|
||||
|
||||
_oop_maps = new OopMapSet();
|
||||
_oop_maps = _needs_transition ? new OopMapSet() : nullptr;
|
||||
address start = __ pc();
|
||||
|
||||
__ enter();
|
||||
@ -200,17 +205,19 @@ void DowncallStubGenerator::generate() {
|
||||
|
||||
_frame_complete = __ pc() - start; // frame build complete.
|
||||
|
||||
__ block_comment("{ thread java2native");
|
||||
address the_pc = __ pc();
|
||||
__ set_last_Java_frame(sp, fp, the_pc, t0);
|
||||
OopMap* map = new OopMap(_frame_size_slots, 0);
|
||||
_oop_maps->add_gc_map(the_pc - start, map);
|
||||
if (_needs_transition) {
|
||||
__ block_comment("{ thread java2native");
|
||||
address the_pc = __ pc();
|
||||
__ set_last_Java_frame(sp, fp, the_pc, t0);
|
||||
OopMap* map = new OopMap(_frame_size_slots, 0);
|
||||
_oop_maps->add_gc_map(the_pc - start, map);
|
||||
|
||||
// State transition
|
||||
__ mv(t0, _thread_in_native);
|
||||
__ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore);
|
||||
__ sw(t0, Address(xthread, JavaThread::thread_state_offset()));
|
||||
__ block_comment("} thread java2native");
|
||||
// State transition
|
||||
__ mv(t0, _thread_in_native);
|
||||
__ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore);
|
||||
__ sw(t0, Address(xthread, JavaThread::thread_state_offset()));
|
||||
__ block_comment("} thread java2native");
|
||||
}
|
||||
|
||||
__ block_comment("{ argument shuffle");
|
||||
arg_shuffle.generate(_masm, shuffle_reg, 0, _abi._shadow_space_bytes, locs);
|
||||
@ -260,80 +267,85 @@ void DowncallStubGenerator::generate() {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
__ block_comment("{ thread native2java");
|
||||
__ mv(t0, _thread_in_native_trans);
|
||||
__ sw(t0, Address(xthread, JavaThread::thread_state_offset()));
|
||||
|
||||
// Force this write out before the read below
|
||||
if (!UseSystemMemoryBarrier) {
|
||||
__ membar(MacroAssembler::AnyAny);
|
||||
}
|
||||
|
||||
Label L_after_safepoint_poll;
|
||||
Label L_safepoint_poll_slow_path;
|
||||
__ safepoint_poll(L_safepoint_poll_slow_path, true /* at_return */, true /* acquire */, false /* in_nmethod */);
|
||||
__ lwu(t0, Address(xthread, JavaThread::suspend_flags_offset()));
|
||||
__ bnez(t0, L_safepoint_poll_slow_path);
|
||||
|
||||
__ bind(L_after_safepoint_poll);
|
||||
|
||||
__ mv(t0, _thread_in_Java);
|
||||
__ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore);
|
||||
__ sw(t0, Address(xthread, JavaThread::thread_state_offset()));
|
||||
|
||||
__ block_comment("reguard stack check");
|
||||
Label L_reguard;
|
||||
Label L_after_reguard;
|
||||
__ lbu(t0, Address(xthread, JavaThread::stack_guard_state_offset()));
|
||||
__ mv(t1, StackOverflow::stack_guard_yellow_reserved_disabled);
|
||||
__ beq(t0, t1, L_reguard);
|
||||
__ bind(L_after_reguard);
|
||||
if (_needs_transition) {
|
||||
__ block_comment("{ thread native2java");
|
||||
__ mv(t0, _thread_in_native_trans);
|
||||
__ sw(t0, Address(xthread, JavaThread::thread_state_offset()));
|
||||
|
||||
__ reset_last_Java_frame(true);
|
||||
__ block_comment("} thread native2java");
|
||||
// Force this write out before the read below
|
||||
if (!UseSystemMemoryBarrier) {
|
||||
__ membar(MacroAssembler::AnyAny);
|
||||
}
|
||||
|
||||
__ safepoint_poll(L_safepoint_poll_slow_path, true /* at_return */, true /* acquire */, false /* in_nmethod */);
|
||||
__ lwu(t0, Address(xthread, JavaThread::suspend_flags_offset()));
|
||||
__ bnez(t0, L_safepoint_poll_slow_path);
|
||||
|
||||
__ bind(L_after_safepoint_poll);
|
||||
|
||||
// change thread state
|
||||
__ mv(t0, _thread_in_Java);
|
||||
__ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore);
|
||||
__ sw(t0, Address(xthread, JavaThread::thread_state_offset()));
|
||||
|
||||
__ block_comment("reguard stack check");
|
||||
__ lbu(t0, Address(xthread, JavaThread::stack_guard_state_offset()));
|
||||
__ mv(t1, StackOverflow::stack_guard_yellow_reserved_disabled);
|
||||
__ beq(t0, t1, L_reguard);
|
||||
__ bind(L_after_reguard);
|
||||
|
||||
__ reset_last_Java_frame(true);
|
||||
__ block_comment("} thread native2java");
|
||||
}
|
||||
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
__ block_comment("{ L_safepoint_poll_slow_path");
|
||||
__ bind(L_safepoint_poll_slow_path);
|
||||
if (_needs_transition) {
|
||||
__ block_comment("{ L_safepoint_poll_slow_path");
|
||||
__ bind(L_safepoint_poll_slow_path);
|
||||
|
||||
if (should_save_return_value) {
|
||||
// Need to save the native result registers around any runtime calls.
|
||||
out_reg_spiller.generate_spill(_masm, spill_offset);
|
||||
}
|
||||
if (should_save_return_value) {
|
||||
// Need to save the native result registers around any runtime calls.
|
||||
out_reg_spiller.generate_spill(_masm, spill_offset);
|
||||
}
|
||||
|
||||
__ mv(c_rarg0, xthread);
|
||||
assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area");
|
||||
__ rt_call(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans));
|
||||
__ mv(c_rarg0, xthread);
|
||||
assert(frame::arg_reg_save_area_bytes == 0, "not expecting frame reg save area");
|
||||
__ rt_call(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans));
|
||||
|
||||
if (should_save_return_value) {
|
||||
out_reg_spiller.generate_fill(_masm, spill_offset);
|
||||
}
|
||||
__ j(L_after_safepoint_poll);
|
||||
__ block_comment("} L_safepoint_poll_slow_path");
|
||||
if (should_save_return_value) {
|
||||
out_reg_spiller.generate_fill(_masm, spill_offset);
|
||||
}
|
||||
__ j(L_after_safepoint_poll);
|
||||
__ block_comment("} L_safepoint_poll_slow_path");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
__ block_comment("{ L_reguard");
|
||||
__ bind(L_reguard);
|
||||
__ block_comment("{ L_reguard");
|
||||
__ bind(L_reguard);
|
||||
|
||||
if (should_save_return_value) {
|
||||
// Need to save the native result registers around any runtime calls.
|
||||
out_reg_spiller.generate_spill(_masm, spill_offset);
|
||||
if (should_save_return_value) {
|
||||
// Need to save the native result registers around any runtime calls.
|
||||
out_reg_spiller.generate_spill(_masm, spill_offset);
|
||||
}
|
||||
|
||||
__ rt_call(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages));
|
||||
|
||||
if (should_save_return_value) {
|
||||
out_reg_spiller.generate_fill(_masm, spill_offset);
|
||||
}
|
||||
|
||||
__ j(L_after_reguard);
|
||||
__ block_comment("} L_reguard");
|
||||
}
|
||||
|
||||
__ rt_call(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages));
|
||||
|
||||
if (should_save_return_value) {
|
||||
out_reg_spiller.generate_fill(_masm, spill_offset);
|
||||
}
|
||||
|
||||
__ j(L_after_reguard);
|
||||
__ block_comment("} L_reguard");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
__ flush();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -44,6 +44,10 @@ bool ABIDescriptor::is_volatile_reg(FloatRegister reg) const {
|
||||
|| _float_additional_volatile_registers.contains(reg);
|
||||
}
|
||||
|
||||
bool ForeignGlobals::is_foreign_linker_supported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
const ABIDescriptor ForeignGlobals::parse_abi_descriptor(jobject jabi) {
|
||||
oop abi_oop = JNIHandles::resolve_non_null(jabi);
|
||||
ABIDescriptor abi;
|
||||
|
||||
@ -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.
|
||||
* Copyright (c) 2020, Red Hat, Inc. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -33,7 +33,8 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature,
|
||||
const GrowableArray<VMStorage>& input_registers,
|
||||
const GrowableArray<VMStorage>& output_registers,
|
||||
bool needs_return_buffer,
|
||||
int captured_state_mask) {
|
||||
int captured_state_mask,
|
||||
bool needs_transition) {
|
||||
Unimplemented();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -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.
|
||||
* Copyright (c) 2020, Red Hat, Inc. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -29,6 +29,10 @@
|
||||
|
||||
class MacroAssembler;
|
||||
|
||||
bool ForeignGlobals::is_foreign_linker_supported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ABIDescriptor ForeignGlobals::parse_abi_descriptor(jobject jabi) {
|
||||
Unimplemented();
|
||||
return {};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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
|
||||
@ -31,7 +31,8 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature,
|
||||
const GrowableArray<VMStorage>& input_registers,
|
||||
const GrowableArray<VMStorage>& output_registers,
|
||||
bool needs_return_buffer,
|
||||
int captured_state_mask) {
|
||||
int captured_state_mask,
|
||||
bool needs_transition) {
|
||||
Unimplemented();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -46,6 +46,7 @@ class DowncallStubGenerator : public StubCodeGenerator {
|
||||
|
||||
bool _needs_return_buffer;
|
||||
int _captured_state_mask;
|
||||
bool _needs_transition;
|
||||
|
||||
int _frame_complete;
|
||||
int _frame_size_slots;
|
||||
@ -59,7 +60,8 @@ public:
|
||||
const GrowableArray<VMStorage>& input_registers,
|
||||
const GrowableArray<VMStorage>& output_registers,
|
||||
bool needs_return_buffer,
|
||||
int captured_state_mask)
|
||||
int captured_state_mask,
|
||||
bool needs_transition)
|
||||
: StubCodeGenerator(buffer, PrintMethodHandleStubs),
|
||||
_signature(signature),
|
||||
_num_args(num_args),
|
||||
@ -69,6 +71,7 @@ public:
|
||||
_output_registers(output_registers),
|
||||
_needs_return_buffer(needs_return_buffer),
|
||||
_captured_state_mask(captured_state_mask),
|
||||
_needs_transition(needs_transition),
|
||||
_frame_complete(0),
|
||||
_frame_size_slots(0),
|
||||
_oop_maps(nullptr) {
|
||||
@ -99,13 +102,15 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature,
|
||||
const GrowableArray<VMStorage>& input_registers,
|
||||
const GrowableArray<VMStorage>& output_registers,
|
||||
bool needs_return_buffer,
|
||||
int captured_state_mask) {
|
||||
int captured_state_mask,
|
||||
bool needs_transition) {
|
||||
int code_size = native_invoker_code_base_size + (num_args * native_invoker_size_per_arg);
|
||||
int locs_size = 1; // can not be zero
|
||||
CodeBuffer code("nep_invoker_blob", code_size, locs_size);
|
||||
DowncallStubGenerator g(&code, signature, num_args, ret_bt, abi,
|
||||
input_registers, output_registers,
|
||||
needs_return_buffer, captured_state_mask);
|
||||
needs_return_buffer, captured_state_mask,
|
||||
needs_transition);
|
||||
g.generate();
|
||||
code.log_section_sizes("nep_invoker_blob");
|
||||
|
||||
@ -161,7 +166,7 @@ void DowncallStubGenerator::generate() {
|
||||
allocated_frame_size += arg_shuffle.out_arg_bytes();
|
||||
|
||||
// when we don't use a return buffer we need to spill the return value around our slow path calls
|
||||
bool should_save_return_value = !_needs_return_buffer;
|
||||
bool should_save_return_value = !_needs_return_buffer && _needs_transition;
|
||||
RegSpiller out_reg_spiller(_output_registers);
|
||||
int spill_rsp_offset = -1;
|
||||
|
||||
@ -190,7 +195,7 @@ void DowncallStubGenerator::generate() {
|
||||
_frame_size_slots += framesize_base + (allocated_frame_size >> LogBytesPerInt);
|
||||
assert(is_even(_frame_size_slots/2), "sp not 16-byte aligned");
|
||||
|
||||
_oop_maps = new OopMapSet();
|
||||
_oop_maps = _needs_transition ? new OopMapSet() : nullptr;
|
||||
address start = __ pc();
|
||||
|
||||
__ enter();
|
||||
@ -200,16 +205,17 @@ void DowncallStubGenerator::generate() {
|
||||
|
||||
_frame_complete = __ pc() - start;
|
||||
|
||||
address the_pc = __ pc();
|
||||
if (_needs_transition) {
|
||||
__ block_comment("{ thread java2native");
|
||||
address the_pc = __ pc();
|
||||
__ set_last_Java_frame(rsp, rbp, (address)the_pc, rscratch1);
|
||||
OopMap* map = new OopMap(_frame_size_slots, 0);
|
||||
_oop_maps->add_gc_map(the_pc - start, map);
|
||||
|
||||
__ block_comment("{ thread java2native");
|
||||
__ set_last_Java_frame(rsp, rbp, (address)the_pc, rscratch1);
|
||||
OopMap* map = new OopMap(_frame_size_slots, 0);
|
||||
_oop_maps->add_gc_map(the_pc - start, map);
|
||||
|
||||
// State transition
|
||||
__ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_native);
|
||||
__ block_comment("} thread java2native");
|
||||
// State transition
|
||||
__ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_native);
|
||||
__ block_comment("} thread java2native");
|
||||
}
|
||||
|
||||
__ block_comment("{ argument shuffle");
|
||||
arg_shuffle.generate(_masm, shuffle_reg, 0, _abi._shadow_space_bytes, locs);
|
||||
@ -263,93 +269,95 @@ void DowncallStubGenerator::generate() {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
__ block_comment("{ thread native2java");
|
||||
__ restore_cpu_control_state_after_jni(rscratch1);
|
||||
|
||||
__ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_native_trans);
|
||||
|
||||
// Force this write out before the read below
|
||||
if (!UseSystemMemoryBarrier) {
|
||||
__ membar(Assembler::Membar_mask_bits(
|
||||
Assembler::LoadLoad | Assembler::LoadStore |
|
||||
Assembler::StoreLoad | Assembler::StoreStore));
|
||||
}
|
||||
|
||||
Label L_after_safepoint_poll;
|
||||
Label L_safepoint_poll_slow_path;
|
||||
|
||||
__ safepoint_poll(L_safepoint_poll_slow_path, r15_thread, true /* at_return */, false /* in_nmethod */);
|
||||
__ cmpl(Address(r15_thread, JavaThread::suspend_flags_offset()), 0);
|
||||
__ jcc(Assembler::notEqual, L_safepoint_poll_slow_path);
|
||||
|
||||
__ bind(L_after_safepoint_poll);
|
||||
|
||||
// change thread state
|
||||
__ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_Java);
|
||||
|
||||
__ block_comment("reguard stack check");
|
||||
Label L_reguard;
|
||||
Label L_after_reguard;
|
||||
__ cmpl(Address(r15_thread, JavaThread::stack_guard_state_offset()), StackOverflow::stack_guard_yellow_reserved_disabled);
|
||||
__ jcc(Assembler::equal, L_reguard);
|
||||
__ bind(L_after_reguard);
|
||||
if (_needs_transition) {
|
||||
__ block_comment("{ thread native2java");
|
||||
__ restore_cpu_control_state_after_jni(rscratch1);
|
||||
|
||||
__ reset_last_Java_frame(r15_thread, true);
|
||||
__ block_comment("} thread native2java");
|
||||
__ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_native_trans);
|
||||
|
||||
// Force this write out before the read below
|
||||
if (!UseSystemMemoryBarrier) {
|
||||
__ membar(Assembler::Membar_mask_bits(
|
||||
Assembler::LoadLoad | Assembler::LoadStore |
|
||||
Assembler::StoreLoad | Assembler::StoreStore));
|
||||
}
|
||||
|
||||
__ safepoint_poll(L_safepoint_poll_slow_path, r15_thread, true /* at_return */, false /* in_nmethod */);
|
||||
__ cmpl(Address(r15_thread, JavaThread::suspend_flags_offset()), 0);
|
||||
__ jcc(Assembler::notEqual, L_safepoint_poll_slow_path);
|
||||
|
||||
__ bind(L_after_safepoint_poll);
|
||||
|
||||
// change thread state
|
||||
__ movl(Address(r15_thread, JavaThread::thread_state_offset()), _thread_in_Java);
|
||||
|
||||
__ block_comment("reguard stack check");
|
||||
__ cmpl(Address(r15_thread, JavaThread::stack_guard_state_offset()), StackOverflow::stack_guard_yellow_reserved_disabled);
|
||||
__ jcc(Assembler::equal, L_reguard);
|
||||
__ bind(L_after_reguard);
|
||||
|
||||
__ reset_last_Java_frame(r15_thread, true);
|
||||
__ block_comment("} thread native2java");
|
||||
}
|
||||
|
||||
__ leave(); // required for proper stackwalking of RuntimeStub frame
|
||||
__ ret(0);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
__ block_comment("{ L_safepoint_poll_slow_path");
|
||||
__ bind(L_safepoint_poll_slow_path);
|
||||
__ vzeroupper();
|
||||
if (_needs_transition) {
|
||||
__ block_comment("{ L_safepoint_poll_slow_path");
|
||||
__ bind(L_safepoint_poll_slow_path);
|
||||
__ vzeroupper();
|
||||
|
||||
if (should_save_return_value) {
|
||||
out_reg_spiller.generate_spill(_masm, spill_rsp_offset);
|
||||
}
|
||||
if (should_save_return_value) {
|
||||
out_reg_spiller.generate_spill(_masm, spill_rsp_offset);
|
||||
}
|
||||
|
||||
__ mov(c_rarg0, r15_thread);
|
||||
__ mov(r12, rsp); // remember sp
|
||||
__ subptr(rsp, frame::arg_reg_save_area_bytes); // windows
|
||||
__ andptr(rsp, -16); // align stack as required by ABI
|
||||
__ call(RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans)));
|
||||
__ mov(rsp, r12); // restore sp
|
||||
__ reinit_heapbase();
|
||||
__ mov(c_rarg0, r15_thread);
|
||||
__ mov(r12, rsp); // remember sp
|
||||
__ subptr(rsp, frame::arg_reg_save_area_bytes); // windows
|
||||
__ andptr(rsp, -16); // align stack as required by ABI
|
||||
__ call(RuntimeAddress(CAST_FROM_FN_PTR(address, JavaThread::check_special_condition_for_native_trans)));
|
||||
__ mov(rsp, r12); // restore sp
|
||||
__ reinit_heapbase();
|
||||
|
||||
if (should_save_return_value) {
|
||||
out_reg_spiller.generate_fill(_masm, spill_rsp_offset);
|
||||
}
|
||||
if (should_save_return_value) {
|
||||
out_reg_spiller.generate_fill(_masm, spill_rsp_offset);
|
||||
}
|
||||
|
||||
__ jmp(L_after_safepoint_poll);
|
||||
__ block_comment("} L_safepoint_poll_slow_path");
|
||||
__ jmp(L_after_safepoint_poll);
|
||||
__ block_comment("} L_safepoint_poll_slow_path");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
__ block_comment("{ L_reguard");
|
||||
__ bind(L_reguard);
|
||||
__ vzeroupper();
|
||||
__ block_comment("{ L_reguard");
|
||||
__ bind(L_reguard);
|
||||
__ vzeroupper();
|
||||
|
||||
if (should_save_return_value) {
|
||||
out_reg_spiller.generate_spill(_masm, spill_rsp_offset);
|
||||
if (should_save_return_value) {
|
||||
out_reg_spiller.generate_spill(_masm, spill_rsp_offset);
|
||||
}
|
||||
|
||||
__ mov(r12, rsp); // remember sp
|
||||
__ subptr(rsp, frame::arg_reg_save_area_bytes); // windows
|
||||
__ andptr(rsp, -16); // align stack as required by ABI
|
||||
__ call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages)));
|
||||
__ mov(rsp, r12); // restore sp
|
||||
__ reinit_heapbase();
|
||||
|
||||
if (should_save_return_value) {
|
||||
out_reg_spiller.generate_fill(_masm, spill_rsp_offset);
|
||||
}
|
||||
|
||||
__ jmp(L_after_reguard);
|
||||
|
||||
__ block_comment("} L_reguard");
|
||||
}
|
||||
|
||||
__ mov(r12, rsp); // remember sp
|
||||
__ subptr(rsp, frame::arg_reg_save_area_bytes); // windows
|
||||
__ andptr(rsp, -16); // align stack as required by ABI
|
||||
__ call(RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::reguard_yellow_pages)));
|
||||
__ mov(rsp, r12); // restore sp
|
||||
__ reinit_heapbase();
|
||||
|
||||
if (should_save_return_value) {
|
||||
out_reg_spiller.generate_fill(_masm, spill_rsp_offset);
|
||||
}
|
||||
|
||||
__ jmp(L_after_reguard);
|
||||
|
||||
__ block_comment("} L_reguard");
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
__ flush();
|
||||
|
||||
@ -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
|
||||
@ -28,6 +28,10 @@
|
||||
|
||||
class MacroAssembler;
|
||||
|
||||
bool ForeignGlobals::is_foreign_linker_supported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ABIDescriptor ForeignGlobals::parse_abi_descriptor(jobject jabi) {
|
||||
Unimplemented();
|
||||
return {};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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
|
||||
@ -30,6 +30,10 @@
|
||||
#include "runtime/sharedRuntime.hpp"
|
||||
#include "utilities/formatBuffer.hpp"
|
||||
|
||||
bool ForeignGlobals::is_foreign_linker_supported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ABIDescriptor::is_volatile_reg(Register reg) const {
|
||||
return _integer_argument_registers.contains(reg)
|
||||
|| _integer_additional_volatile_registers.contains(reg);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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
|
||||
@ -31,7 +31,8 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature,
|
||||
const GrowableArray<VMStorage>& input_registers,
|
||||
const GrowableArray<VMStorage>& output_registers,
|
||||
bool needs_return_buffer,
|
||||
int captured_state_mask) {
|
||||
int captured_state_mask,
|
||||
bool needs_transition) {
|
||||
Unimplemented();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 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
|
||||
@ -28,6 +28,10 @@
|
||||
|
||||
class MacroAssembler;
|
||||
|
||||
bool ForeignGlobals::is_foreign_linker_supported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ABIDescriptor ForeignGlobals::parse_abi_descriptor(jobject jabi) {
|
||||
ShouldNotCallThis();
|
||||
return {};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2009, 2021, Red Hat, Inc.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -32,7 +32,7 @@
|
||||
|
||||
#define SUPPORT_MONITOR_COUNT
|
||||
|
||||
#ifndef FFI_GO_CLOSURES
|
||||
#ifdef __APPLE__
|
||||
#define FFI_GO_CLOSURES 0
|
||||
#endif
|
||||
|
||||
|
||||
@ -174,6 +174,9 @@ JVM_IsPreviewEnabled(void);
|
||||
JNIEXPORT jboolean JNICALL
|
||||
JVM_IsContinuationsSupported(void);
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
JVM_IsForeignLinkerSupported(void);
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
JVM_InitializeFromArchive(JNIEnv* env, jclass cls);
|
||||
|
||||
|
||||
@ -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
|
||||
@ -41,12 +41,12 @@ void DowncallLinker::capture_state(int32_t* value_ptr, int captured_state_mask)
|
||||
#ifdef _WIN64
|
||||
if (captured_state_mask & GET_LAST_ERROR) {
|
||||
*value_ptr = GetLastError();
|
||||
value_ptr++;
|
||||
}
|
||||
value_ptr++;
|
||||
if (captured_state_mask & WSA_GET_LAST_ERROR) {
|
||||
*value_ptr = WSAGetLastError();
|
||||
value_ptr++;
|
||||
}
|
||||
value_ptr++;
|
||||
#endif
|
||||
if (captured_state_mask & ERRNO) {
|
||||
*value_ptr = errno;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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,7 +37,8 @@ public:
|
||||
const GrowableArray<VMStorage>& input_registers,
|
||||
const GrowableArray<VMStorage>& output_registers,
|
||||
bool needs_return_buffer,
|
||||
int captured_state_mask);
|
||||
int captured_state_mask,
|
||||
bool needs_transition);
|
||||
|
||||
static void capture_state(int32_t* value_ptr, int captured_state_mask);
|
||||
};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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
|
||||
@ -76,6 +76,8 @@ private:
|
||||
static void parse_register_array(objArrayOop jarray, StorageType type_index, GrowableArray<T>& array, T (*converter)(int));
|
||||
|
||||
public:
|
||||
static bool is_foreign_linker_supported();
|
||||
|
||||
static const ABIDescriptor parse_abi_descriptor(jobject jabi);
|
||||
static const CallRegs parse_call_regs(jobject jconv);
|
||||
static VMStorage parse_vmstorage(oop storage);
|
||||
|
||||
@ -63,6 +63,7 @@
|
||||
#include "oops/objArrayKlass.hpp"
|
||||
#include "oops/objArrayOop.inline.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "prims/foreignGlobals.hpp"
|
||||
#include "prims/jvm_misc.hpp"
|
||||
#include "prims/jvmtiExport.hpp"
|
||||
#include "prims/jvmtiThreadState.inline.hpp"
|
||||
@ -3462,6 +3463,10 @@ JVM_LEAF(jboolean, JVM_IsContinuationsSupported(void))
|
||||
return VMContinuations ? JNI_TRUE : JNI_FALSE;
|
||||
JVM_END
|
||||
|
||||
JVM_LEAF(jboolean, JVM_IsForeignLinkerSupported(void))
|
||||
return ForeignGlobals::is_foreign_linker_supported() ? JNI_TRUE : JNI_FALSE;
|
||||
JVM_END
|
||||
|
||||
// String support ///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str))
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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,7 +37,8 @@
|
||||
|
||||
JNI_ENTRY(jlong, NEP_makeDowncallStub(JNIEnv* env, jclass _unused, jobject method_type, jobject jabi,
|
||||
jobjectArray arg_moves, jobjectArray ret_moves,
|
||||
jboolean needs_return_buffer, jint captured_state_mask))
|
||||
jboolean needs_return_buffer, jint captured_state_mask,
|
||||
jboolean needs_transition))
|
||||
ResourceMark rm;
|
||||
const ABIDescriptor abi = ForeignGlobals::parse_abi_descriptor(jabi);
|
||||
|
||||
@ -77,7 +78,8 @@ JNI_ENTRY(jlong, NEP_makeDowncallStub(JNIEnv* env, jclass _unused, jobject metho
|
||||
|
||||
return (jlong) DowncallLinker::make_downcall_stub(basic_type, pslots, ret_bt, abi,
|
||||
input_regs, output_regs,
|
||||
needs_return_buffer, captured_state_mask)->code_begin();
|
||||
needs_return_buffer, captured_state_mask,
|
||||
needs_transition)->code_begin();
|
||||
JNI_END
|
||||
|
||||
JNI_ENTRY(jboolean, NEP_freeDowncallStub(JNIEnv* env, jclass _unused, jlong invoker))
|
||||
@ -97,7 +99,7 @@ JNI_END
|
||||
#define VM_STORAGE_ARR "[Ljdk/internal/foreign/abi/VMStorage;"
|
||||
|
||||
static JNINativeMethod NEP_methods[] = {
|
||||
{CC "makeDowncallStub", CC "(" METHOD_TYPE ABI_DESC VM_STORAGE_ARR VM_STORAGE_ARR "ZI)J", FN_PTR(NEP_makeDowncallStub)},
|
||||
{CC "makeDowncallStub", CC "(" METHOD_TYPE ABI_DESC VM_STORAGE_ARR VM_STORAGE_ARR "ZIZ)J", FN_PTR(NEP_makeDowncallStub)},
|
||||
{CC "freeDowncallStub0", CC "(J)Z", FN_PTR(NEP_freeDowncallStub)},
|
||||
};
|
||||
|
||||
|
||||
@ -75,6 +75,7 @@ JavaThread* UpcallLinker::maybe_attach_and_get_thread() {
|
||||
// modelled after JavaCallWrapper::JavaCallWrapper
|
||||
JavaThread* UpcallLinker::on_entry(UpcallStub::FrameData* context) {
|
||||
JavaThread* thread = maybe_attach_and_get_thread();
|
||||
guarantee(thread->thread_state() == _thread_in_native, "wrong thread state for upcall");
|
||||
context->thread = thread;
|
||||
|
||||
assert(thread->can_call_java(), "must be able to call Java");
|
||||
|
||||
130
src/java.base/share/classes/java/lang/foreign/AddressLayout.java
Normal file
130
src/java.base/share/classes/java/lang/foreign/AddressLayout.java
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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 java.lang.foreign;
|
||||
|
||||
import jdk.internal.foreign.layout.ValueLayouts;
|
||||
import jdk.internal.javac.PreviewFeature;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
|
||||
import java.lang.foreign.Linker.Option;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A value layout used to model the address of some region of memory. The carrier associated with an address layout is
|
||||
* {@code MemorySegment.class}. The size and alignment of an address layout are platform dependent
|
||||
* (e.g. on a 64-bit platform, the size and alignment of an address layout are set to 64 bits).
|
||||
* <p>
|
||||
* An address layout may optionally feature a {@linkplain #targetLayout() target layout}. An address layout with
|
||||
* target layout {@code T} can be used to model the address of a region of memory whose layout is {@code T}.
|
||||
* For instance, an address layout with target layout {@link ValueLayout#JAVA_INT} can be used to model the address
|
||||
* of a region of memory that is 4 bytes long. Specifying a target layout can be useful in the following situations:
|
||||
* <ul>
|
||||
* <li>When accessing a memory segment that has been obtained by reading an address from another
|
||||
* memory segment, e.g. using {@link MemorySegment#getAtIndex(AddressLayout, long)};</li>
|
||||
* <li>When creating a downcall method handle, using {@link Linker#downcallHandle(FunctionDescriptor, Option...)};
|
||||
* <li>When creating an upcall stub, using {@link Linker#upcallStub(MethodHandle, FunctionDescriptor, Arena, Option...)}.
|
||||
* </ul>
|
||||
*
|
||||
* @see #ADDRESS
|
||||
* @see #ADDRESS_UNALIGNED
|
||||
* @since 19
|
||||
*/
|
||||
@PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
|
||||
public sealed interface AddressLayout extends ValueLayout permits ValueLayouts.OfAddressImpl {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
AddressLayout withName(String name);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
AddressLayout withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
AddressLayout withBitAlignment(long bitAlignment);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
AddressLayout withOrder(ByteOrder order);
|
||||
|
||||
/**
|
||||
* Returns an address layout with the same carrier, alignment constraint, name and order as this address layout,
|
||||
* but associated with the specified target layout. The returned address layout allows raw addresses to be accessed
|
||||
* as {@linkplain MemorySegment memory segments} whose size is set to the size of the specified layout. Moreover,
|
||||
* if the accessed raw address is not compatible with the alignment constraint in the provided layout,
|
||||
* {@linkplain IllegalArgumentException} will be thrown.
|
||||
* @apiNote
|
||||
* This method can also be used to create an address layout which, when used, creates native memory
|
||||
* segments with maximal size (e.g. {@linkplain Long#MAX_VALUE}). This can be done by using a target sequence
|
||||
* layout with unspecified size, as follows:
|
||||
* {@snippet lang = java:
|
||||
* AddressLayout addressLayout = ...
|
||||
* AddressLayout unboundedLayout = addressLayout.withTargetLayout(
|
||||
* MemoryLayout.sequenceLayout(ValueLayout.JAVA_BYTE));
|
||||
*}
|
||||
* <p>
|
||||
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
|
||||
* Restricted methods are unsafe, and, if used incorrectly, their use might crash
|
||||
* the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
|
||||
* restricted methods, and use safe and supported functionalities, where possible.
|
||||
*
|
||||
* @param layout the target layout.
|
||||
* @return an address layout with same characteristics as this layout, but with the provided target layout.
|
||||
* @throws IllegalCallerException If the caller is in a module that does not have native access enabled.
|
||||
* @see #targetLayout()
|
||||
*/
|
||||
@CallerSensitive
|
||||
AddressLayout withTargetLayout(MemoryLayout layout);
|
||||
|
||||
/**
|
||||
* Returns an address layout with the same carrier, alignment constraint, name and order as this address layout,
|
||||
* but without any specified target layout.
|
||||
* <p>
|
||||
* This can be useful to compare two address layouts that have different target layouts, but are otherwise equal.
|
||||
*
|
||||
* @return an address layout with same characteristics as this layout, but with no target layout.
|
||||
* @see #targetLayout()
|
||||
*/
|
||||
AddressLayout withoutTargetLayout();
|
||||
|
||||
/**
|
||||
* {@return the target layout associated with this address layout (if any)}.
|
||||
*/
|
||||
Optional<MemoryLayout> targetLayout();
|
||||
|
||||
}
|
||||
@ -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
|
||||
@ -27,40 +27,105 @@ package java.lang.foreign;
|
||||
|
||||
import jdk.internal.foreign.MemorySessionImpl;
|
||||
import jdk.internal.javac.PreviewFeature;
|
||||
import jdk.internal.ref.CleanerFactory;
|
||||
|
||||
import java.lang.foreign.MemorySegment.Scope;
|
||||
|
||||
/**
|
||||
* An arena controls the lifecycle of memory segments, providing both flexible allocation and timely deallocation.
|
||||
* An arena controls the lifecycle of native memory segments, providing both flexible allocation and timely deallocation.
|
||||
* <p>
|
||||
* An arena has a {@linkplain #scope() scope}, called the arena scope. When the arena is {@linkplain #close() closed},
|
||||
* the arena scope is no longer {@linkplain SegmentScope#isAlive() alive}. As a result, all the
|
||||
* segments associated with the arena scope are invalidated, safely and atomically, their backing memory regions are
|
||||
* deallocated (where applicable) and can no longer be accessed after the arena is closed:
|
||||
* An arena has a {@linkplain MemorySegment.Scope scope} - the <em>arena scope</em>. All the segments allocated
|
||||
* by the arena are associated with the arena scope. As such, the arena determines the temporal bounds
|
||||
* of all the memory segments allocated by it.
|
||||
* <p>
|
||||
* Moreover, an arena also determines whether access to memory segments allocated by it should be
|
||||
* {@linkplain MemorySegment#isAccessibleBy(Thread) restricted} to specific threads.
|
||||
* An arena is a {@link SegmentAllocator} and features several allocation methods that can be used by clients
|
||||
* to obtain native segments.
|
||||
* <p>
|
||||
* The simplest arena is the {@linkplain Arena#global() global arena}. The global arena
|
||||
* features an <em>unbounded lifetime</em>. As such, native segments allocated with the global arena are always
|
||||
* accessible and their backing regions of memory are never deallocated. Moreover, memory segments allocated with the
|
||||
* global arena can be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} from any thread.
|
||||
* {@snippet lang = java:
|
||||
* MemorySegment segment = Arena.global().allocate(100, 1);
|
||||
* ...
|
||||
* // segment is never deallocated!
|
||||
*}
|
||||
* <p>
|
||||
* Alternatively, clients can obtain an {@linkplain Arena#ofAuto() automatic arena}, that is an arena
|
||||
* which features a <em>bounded lifetime</em> that is managed, automatically, by the garbage collector. As such, the regions
|
||||
* of memory backing memory segments allocated with the automatic arena are deallocated at some unspecified time
|
||||
* <em>after</em> the automatic arena (and all the segments allocated by it) become
|
||||
* <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>, as shown below:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* try (Arena arena = Arena.openConfined()) {
|
||||
* MemorySegment segment = MemorySegment.allocateNative(100, arena.scope());
|
||||
* ...
|
||||
* } // memory released here
|
||||
* MemorySegment segment = Arena.ofAuto().allocate(100, 1);
|
||||
* ...
|
||||
* segment = null; // the segment region becomes available for deallocation after this point
|
||||
*}
|
||||
*
|
||||
* Furthermore, an arena is a {@link SegmentAllocator}. All the segments {@linkplain #allocate(long, long) allocated} by the
|
||||
* arena are associated with the arena scope. This makes arenas extremely useful when interacting with foreign code, as shown below:
|
||||
* Memory segments allocated with an automatic arena can also be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} from any thread.
|
||||
* <p>
|
||||
* Rather than leaving deallocation in the hands of the Java runtime, clients will often wish to exercise control over
|
||||
* the timing of deallocation for regions of memory that back memory segments. Two kinds of arenas support this,
|
||||
* namely {@linkplain #ofConfined() confined} and {@linkplain #ofShared() shared} arenas. They both feature
|
||||
* bounded lifetimes that are managed manually. For instance, the lifetime of a confined arena starts when the confined
|
||||
* arena is created, and ends when the confined arena is {@linkplain #close() closed}. As a result, the regions of memory
|
||||
* backing memory segments allocated with a confined arena are deallocated when the confined arena is closed.
|
||||
* When this happens, all the segments allocated with the confined arena are invalidated, and subsequent access
|
||||
* operations on these segments will fail {@link IllegalStateException}:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* try (Arena arena = Arena.openConfined()) {
|
||||
* MemorySegment nativeArray = arena.allocateArray(ValueLayout.JAVA_INT, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
|
||||
* MemorySegment nativeString = arena.allocateUtf8String("Hello!");
|
||||
* MemorySegment upcallStub = linker.upcallStub(handle, desc, arena.scope());
|
||||
* MemorySegment segment = null;
|
||||
* try (Arena arena = Arena.ofConfined()) {
|
||||
* segment = arena.allocate(100);
|
||||
* ...
|
||||
* } // memory released here
|
||||
* } // segment region deallocated here
|
||||
* segment.get(ValueLayout.JAVA_BYTE, 0); // throws IllegalStateException
|
||||
*}
|
||||
*
|
||||
* Memory segments allocated with a {@linkplain #ofConfined() confined arena} can only be accessed (and closed) by the
|
||||
* thread that created the arena. If access to a memory segment from multiple threads is required, clients can allocate
|
||||
* segments in a {@linkplain #ofShared() shared arena} instead.
|
||||
* <p>
|
||||
* The characteristics of the various arenas are summarized in the following table:
|
||||
*
|
||||
* <blockquote><table class="plain">
|
||||
* <caption style="display:none">Arenas characteristics</caption>
|
||||
* <thead>
|
||||
* <tr>
|
||||
* <th scope="col">Kind</th>
|
||||
* <th scope="col">Bounded lifetime</th>
|
||||
* <th scope="col">Explicitly closeable</th>
|
||||
* <th scope="col">Accessible from multiple threads</th>
|
||||
* </tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><th scope="row" style="font-weight:normal">Global</th>
|
||||
* <td style="text-align:center;">No</td>
|
||||
* <td style="text-align:center;">No</td>
|
||||
* <td style="text-align:center;">Yes</td></tr>
|
||||
* <tr><th scope="row" style="font-weight:normal">Automatic</th>
|
||||
* <td style="text-align:center;">Yes</td>
|
||||
* <td style="text-align:center;">No</td>
|
||||
* <td style="text-align:center;">Yes</td></tr>
|
||||
* <tr><th scope="row" style="font-weight:normal">Confined</th>
|
||||
* <td style="text-align:center;">Yes</td>
|
||||
* <td style="text-align:center;">Yes</td>
|
||||
* <td style="text-align:center;">No</td></tr>
|
||||
* <tr><th scope="row" style="font-weight:normal">Shared</th>
|
||||
* <td style="text-align:center;">Yes</td>
|
||||
* <td style="text-align:center;">Yes</td>
|
||||
* <td style="text-align:center;">Yes</td></tr>
|
||||
* </tbody>
|
||||
* </table></blockquote>
|
||||
*
|
||||
* <h2 id = "thread-confinement">Safety and thread-confinement</h2>
|
||||
*
|
||||
* Arenas provide strong temporal safety guarantees: a memory segment allocated by an arena cannot be accessed
|
||||
* <em>after</em> the arena has been closed. The cost of providing this guarantee varies based on the
|
||||
* number of threads that have access to the memory segments allocated by the arena. For instance, if an arena
|
||||
* is always created and closed by one thread, and the memory segments associated with the arena's scope are always
|
||||
* is always created and closed by one thread, and the memory segments allocated by the arena are always
|
||||
* accessed by that same thread, then ensuring correctness is trivial.
|
||||
* <p>
|
||||
* Conversely, if an arena allocates segments that can be accessed by multiple threads, or if the arena can be closed
|
||||
@ -70,34 +135,120 @@ import jdk.internal.javac.PreviewFeature;
|
||||
* impact, arenas are divided into <em>thread-confined</em> arenas, and <em>shared</em> arenas.
|
||||
* <p>
|
||||
* Confined arenas, support strong thread-confinement guarantees. Upon creation, they are assigned an
|
||||
* {@linkplain #isCloseableBy(Thread) owner thread}, typically the thread which initiated the creation operation.
|
||||
* The segments created by a confined arena can only be {@linkplain SegmentScope#isAccessibleBy(Thread) accessed}
|
||||
* <em>owner thread</em>, typically the thread which initiated the creation operation.
|
||||
* The segments created by a confined arena can only be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed}
|
||||
* by the owner thread. Moreover, any attempt to close the confined arena from a thread other than the owner thread will
|
||||
* fail with {@link WrongThreadException}.
|
||||
* <p>
|
||||
* Shared arenas, on the other hand, have no owner thread. The segments created by a shared arena
|
||||
* can be {@linkplain SegmentScope#isAccessibleBy(Thread) accessed} by any thread. This might be useful when
|
||||
* can be {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by any thread. This might be useful when
|
||||
* multiple threads need to access the same memory segment concurrently (e.g. in the case of parallel processing).
|
||||
* Moreover, a shared arena {@linkplain #isCloseableBy(Thread) can be closed} by any thread.
|
||||
* Moreover, a shared arena can be closed by any thread.
|
||||
*
|
||||
* <h2 id = "custom-arenas">Custom arenas</h2>
|
||||
*
|
||||
* Clients can define custom arenas to implement more efficient allocation strategies, or to have better control over
|
||||
* when (and by whom) an arena can be closed. As an example, the following code defines a <em>slicing arena</em> that behaves
|
||||
* like a confined arena (i.e., single-threaded access), but internally uses a
|
||||
* {@linkplain SegmentAllocator#slicingAllocator(MemorySegment) slicing allocator} to respond to allocation requests.
|
||||
* When the slicing arena is closed, the underlying confined arena is also closed; this will invalidate all segments
|
||||
* allocated with the slicing arena (since the scope of the slicing arena is the same as that of the underlying
|
||||
* confined arena):
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* class SlicingArena implements Arena {
|
||||
* final Arena arena = Arena.ofConfined();
|
||||
* final SegmentAllocator slicingAllocator;
|
||||
*
|
||||
* SlicingArena(long size) {
|
||||
* slicingAllocator = SegmentAllocator.slicingAllocator(arena.allocate(size));
|
||||
* }
|
||||
*
|
||||
* public void allocate(long byteSize, long byteAlignment) {
|
||||
* return slicingAllocator.allocate(byteSize, byteAlignment);
|
||||
* }
|
||||
*
|
||||
* public MemorySegment.Scope scope() {
|
||||
* return arena.scope();
|
||||
* }
|
||||
*
|
||||
* public void close() {
|
||||
* return arena.close();
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* In other words, a slicing arena provides a vastly more efficient and scalable allocation strategy, while still retaining
|
||||
* the timely deallocation guarantee provided by the underlying confined arena:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* try (Arena slicingArena = new SlicingArena(1000)) {
|
||||
* for (int i = 0 ; i < 10 ; i++) {
|
||||
* MemorySegment s = slicingArena.allocateArray(JAVA_INT, 1, 2, 3, 4, 5);
|
||||
* ...
|
||||
* }
|
||||
* } // all memory allocated is released here
|
||||
* }
|
||||
*
|
||||
* @implSpec
|
||||
* Implementations of this interface are thread-safe.
|
||||
*
|
||||
* @see MemorySegment
|
||||
*
|
||||
* @since 20
|
||||
*/
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
||||
public interface Arena extends SegmentAllocator, AutoCloseable {
|
||||
|
||||
/**
|
||||
* Creates a new arena that is managed, automatically, by the garbage collector.
|
||||
* Segments obtained with the returned arena can be
|
||||
* {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by any thread.
|
||||
* Calling {@link #close()} on the returned arena will result in an {@link UnsupportedOperationException}.
|
||||
*
|
||||
* @return a new arena that is managed, automatically, by the garbage collector.
|
||||
*/
|
||||
static Arena ofAuto() {
|
||||
return MemorySessionImpl.createImplicit(CleanerFactory.cleaner()).asArena();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the global arena. Segments obtained with the global arena can be
|
||||
* {@linkplain MemorySegment#isAccessibleBy(Thread) accessed} by any thread.
|
||||
* Calling {@link #close()} on the returned arena will result in an {@link UnsupportedOperationException}.
|
||||
*
|
||||
* @return the global arena.
|
||||
*/
|
||||
static Arena global() {
|
||||
class Holder {
|
||||
static final Arena GLOBAL = MemorySessionImpl.GLOBAL.asArena();
|
||||
}
|
||||
return Holder.GLOBAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a new confined arena, owned by the current thread}
|
||||
*/
|
||||
static Arena ofConfined() {
|
||||
return MemorySessionImpl.createConfined(Thread.currentThread()).asArena();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a new shared arena}
|
||||
*/
|
||||
static Arena ofShared() {
|
||||
return MemorySessionImpl.createShared().asArena();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a native memory segment with the given size (in bytes) and alignment constraint (in bytes).
|
||||
* The returned segment is associated with the arena scope.
|
||||
* The returned segment is associated with this {@linkplain #scope() arena scope}.
|
||||
* The segment's {@link MemorySegment#address() address} is the starting address of the
|
||||
* allocated off-heap memory region backing the segment, and the address is
|
||||
* aligned according the provided alignment constraint.
|
||||
*
|
||||
* @implSpec
|
||||
* The default implementation of this method is equivalent to the following code:
|
||||
* {@snippet lang = java:
|
||||
* MemorySegment.allocateNative(bytesSize, byteAlignment, scope());
|
||||
*}
|
||||
* More generally implementations of this method must return a native segment featuring the requested size,
|
||||
* Implementations of this method must return a native segment featuring the requested size,
|
||||
* and that is compatible with the provided alignment constraint. Furthermore, for any two segments
|
||||
* {@code S1, S2} returned by this method, the following invariant must hold:
|
||||
*
|
||||
@ -110,57 +261,43 @@ public interface Arena extends SegmentAllocator, AutoCloseable {
|
||||
* @return a new native memory segment.
|
||||
* @throws IllegalArgumentException if {@code bytesSize < 0}, {@code alignmentBytes <= 0}, or if {@code alignmentBytes}
|
||||
* is not a power of 2.
|
||||
* @throws IllegalStateException if the arena has already been {@linkplain #close() closed}.
|
||||
* @throws WrongThreadException if this method is called from a thread {@code T},
|
||||
* such that {@code scope().isAccessibleBy(T) == false}.
|
||||
* @see MemorySegment#allocateNative(long, long, SegmentScope)
|
||||
* @throws IllegalStateException if this arena has already been {@linkplain #close() closed}.
|
||||
* @throws WrongThreadException if this arena is confined, and this method is called from a thread {@code T}
|
||||
* other than the arena owner thread.
|
||||
*/
|
||||
@Override
|
||||
default MemorySegment allocate(long byteSize, long byteAlignment) {
|
||||
return MemorySegment.allocateNative(byteSize, byteAlignment, scope());
|
||||
return ((MemorySessionImpl)scope()).allocate(byteSize, byteAlignment);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the arena scope}
|
||||
*/
|
||||
SegmentScope scope();
|
||||
Scope scope();
|
||||
|
||||
/**
|
||||
* Closes this arena. If this method completes normally, the arena scope is no longer {@linkplain SegmentScope#isAlive() alive},
|
||||
* Closes this arena. If this method completes normally, the arena scope is no longer {@linkplain Scope#isAlive() alive},
|
||||
* and all the memory segments associated with it can no longer be accessed. Furthermore, any off-heap region of memory backing the
|
||||
* segments associated with that scope are also released.
|
||||
* segments obtained from this arena are also released.
|
||||
*
|
||||
* @apiNote This operation is not idempotent; that is, closing an already closed arena <em>always</em> results in an
|
||||
* exception being thrown. This reflects a deliberate design choice: failure to close an arena might reveal a bug
|
||||
* in the underlying application logic.
|
||||
*
|
||||
* @see SegmentScope#isAlive()
|
||||
* @implSpec If this method completes normally, then {@code this.scope().isAlive() == false}.
|
||||
* Implementations are allowed to throw {@link UnsupportedOperationException} if an explicit close operation is
|
||||
* not supported.
|
||||
*
|
||||
* @see Scope#isAlive()
|
||||
*
|
||||
* @throws IllegalStateException if the arena has already been closed.
|
||||
* @throws IllegalStateException if the arena scope is {@linkplain SegmentScope#whileAlive(Runnable) kept alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread {@code T},
|
||||
* such that {@code isCloseableBy(T) == false}.
|
||||
* @throws IllegalStateException if a segment associated with this arena is being accessed concurrently, e.g.
|
||||
* by a {@linkplain Linker#downcallHandle(FunctionDescriptor, Linker.Option...) downcall method handle}.
|
||||
* @throws WrongThreadException if this arena is confined, and this method is called from a thread {@code T}
|
||||
* other than the arena owner thread.
|
||||
* @throws UnsupportedOperationException if this arena does not support explicit closure.
|
||||
*/
|
||||
@Override
|
||||
void close();
|
||||
|
||||
/**
|
||||
* {@return {@code true} if the provided thread can close this arena}
|
||||
* @param thread the thread to be tested.
|
||||
*/
|
||||
boolean isCloseableBy(Thread thread);
|
||||
|
||||
/**
|
||||
* {@return a new confined arena, owned by the current thread}
|
||||
*/
|
||||
static Arena openConfined() {
|
||||
return MemorySessionImpl.createConfined(Thread.currentThread()).asArena();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a new shared arena}
|
||||
*/
|
||||
static Arena openShared() {
|
||||
return MemorySessionImpl.createShared().asArena();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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,7 +37,7 @@ import jdk.internal.javac.PreviewFeature;
|
||||
* A function descriptor models the signature of foreign functions. A function descriptor is made up of zero or more
|
||||
* argument layouts and zero or one return layout. A function descriptor is typically used when creating
|
||||
* {@linkplain Linker#downcallHandle(MemorySegment, FunctionDescriptor, Linker.Option...) downcall method handles} or
|
||||
* {@linkplain Linker#upcallStub(MethodHandle, FunctionDescriptor, SegmentScope) upcall stubs}.
|
||||
* {@linkplain Linker#upcallStub(MethodHandle, FunctionDescriptor, Arena, Linker.Option...) upcall stubs}.
|
||||
*
|
||||
* @implSpec
|
||||
* Implementing classes are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 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
|
||||
@ -63,5 +63,14 @@ public sealed interface GroupLayout extends MemoryLayout permits StructLayout, U
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
GroupLayout withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
* @throws IllegalArgumentException if {@code bitAlignment} is less than {@code M}, where {@code M} is the maximum alignment
|
||||
* constraint in any of the member layouts associated with this group layout.
|
||||
*/
|
||||
@Override
|
||||
GroupLayout withBitAlignment(long bitAlignment);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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
|
||||
@ -34,8 +34,10 @@ import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@ -54,46 +56,340 @@ import java.util.stream.Stream;
|
||||
* <li>A linker allows Java code to link against foreign functions, via
|
||||
* {@linkplain #downcallHandle(MemorySegment, FunctionDescriptor, Option...) downcall method handles}; and</li>
|
||||
* <li>A linker allows foreign functions to call Java method handles,
|
||||
* via the generation of {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, SegmentScope) upcall stubs}.</li>
|
||||
* via the generation of {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, Arena, Option...) upcall stubs}.</li>
|
||||
* </ul>
|
||||
* In addition, a linker provides a way to look up foreign functions in libraries that conform to the ABI. Each linker
|
||||
* chooses a set of libraries that are commonly used on the OS and processor combination associated with the ABI.
|
||||
* For example, a linker for Linux/x64 might choose two libraries: {@code libc} and {@code libm}. The functions in these
|
||||
* libraries are exposed via a {@linkplain #defaultLookup() symbol lookup}.
|
||||
* <p>
|
||||
* The {@link #nativeLinker()} method provides a linker for the ABI associated with the OS and processor where the Java runtime
|
||||
* is currently executing. This linker also provides access, via its {@linkplain #defaultLookup() default lookup},
|
||||
* to the native libraries loaded with the Java runtime.
|
||||
*
|
||||
* <h2 id="downcall-method-handles">Downcall method handles</h2>
|
||||
* <h2 id="native-linker">Calling native functions</h2>
|
||||
*
|
||||
* {@linkplain #downcallHandle(FunctionDescriptor, Option...) Linking a foreign function} is a process which requires a function descriptor,
|
||||
* a set of memory layouts which, together, specify the signature of the foreign function to be linked, and returns,
|
||||
* when complete, a downcall method handle, that is, a method handle that can be used to invoke the target foreign function.
|
||||
* <p>
|
||||
* The Java {@linkplain java.lang.invoke.MethodType method type} associated with the returned method handle is
|
||||
* {@linkplain FunctionDescriptor#toMethodType() derived} from the argument and return layouts in the function descriptor.
|
||||
* The downcall method handle type, might then be decorated by additional leading parameters, in the given order if both are present:
|
||||
* <ul>
|
||||
* <li>If the downcall method handle is created {@linkplain #downcallHandle(FunctionDescriptor, Option...) without specifying a target address},
|
||||
* the downcall method handle type features a leading parameter of type {@link MemorySegment}, from which the
|
||||
* address of the target foreign function can be derived.</li>
|
||||
* <li>If the function descriptor's return layout is a group layout, the resulting downcall method handle accepts
|
||||
* an additional leading parameter of type {@link SegmentAllocator}, which is used by the linker runtime to allocate the
|
||||
* memory region associated with the struct returned by the downcall method handle.</li>
|
||||
* </ul>
|
||||
* The {@linkplain #nativeLinker() native linker} can be used to link against functions
|
||||
* defined in C libraries (native functions). Suppose we wish to downcall from Java to the {@code strlen} function
|
||||
* defined in the standard C library:
|
||||
* {@snippet lang = c:
|
||||
* size_t strlen(const char *s);
|
||||
* }
|
||||
* A downcall method handle that exposes {@code strlen} is obtained, using the native linker, as follows:
|
||||
*
|
||||
* <h2 id="upcall-stubs">Upcall stubs</h2>
|
||||
* {@snippet lang = java:
|
||||
* Linker linker = Linker.nativeLinker();
|
||||
* MethodHandle strlen = linker.downcallHandle(
|
||||
* linker.defaultLookup().find("strlen").get(),
|
||||
* FunctionDescriptor.of(JAVA_LONG, ADDRESS)
|
||||
* );
|
||||
* }
|
||||
*
|
||||
* {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, SegmentScope) Creating an upcall stub} requires a method
|
||||
* handle and a function descriptor; in this case, the set of memory layouts in the function descriptor
|
||||
* specify the signature of the function pointer associated with the upcall stub.
|
||||
* Note how the native linker also provides access, via its {@linkplain #defaultLookup() default lookup},
|
||||
* to the native functions defined by the C libraries loaded with the Java runtime. Above, the default lookup
|
||||
* is used to search the address of the {@code strlen} native function. That address is then passed, along with
|
||||
* a <em>platform-dependent description</em> of the signature of the function expressed as a
|
||||
* {@link FunctionDescriptor} (more on that below) to the native linker's
|
||||
* {@link #downcallHandle(MemorySegment, FunctionDescriptor, Option...)} method.
|
||||
* The obtained downcall method handle is then invoked as follows:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* try (Arena arena = Arena.openConfined()) {
|
||||
* MemorySegment str = arena.allocateUtf8String("Hello");
|
||||
* long len = strlen.invoke(str); // 5
|
||||
* }
|
||||
* }
|
||||
* <h3 id="describing-c-sigs">Describing C signatures</h3>
|
||||
*
|
||||
* When interacting with the native linker, clients must provide a platform-dependent description of the signature
|
||||
* of the C function they wish to link against. This description, a {@link FunctionDescriptor function descriptor},
|
||||
* defines the layouts associated with the parameter types and return type (if any) of the C function.
|
||||
* <p>
|
||||
* The type of the provided method handle's type has to match the method type associated with the upcall stub,
|
||||
* which is {@linkplain FunctionDescriptor#toMethodType() derived} from the provided function descriptor.
|
||||
* Scalar C types such as {@code bool}, {@code int} are modelled as {@linkplain ValueLayout value layouts}
|
||||
* of a suitable carrier. The mapping between a scalar type and its corresponding layout is dependent on the ABI
|
||||
* implemented by the native linker. For instance, the C type {@code long} maps to the layout constant
|
||||
* {@link ValueLayout#JAVA_LONG} on Linux/x64, but maps to the layout constant {@link ValueLayout#JAVA_INT} on
|
||||
* Windows/x64. Similarly, the C type {@code size_t} maps to the layout constant {@link ValueLayout#JAVA_LONG}
|
||||
* on 64-bit platforms, but maps to the layout constant {@link ValueLayout#JAVA_INT} on 32-bit platforms.
|
||||
* <p>
|
||||
* Upcall stubs are modelled by instances of type {@link MemorySegment}; upcall stubs can be passed by reference to other
|
||||
* downcall method handles and, they are released via their associated {@linkplain SegmentScope scope}.
|
||||
* Composite types are modelled as {@linkplain GroupLayout group layouts}. More specifically, a C {@code struct} type
|
||||
* maps to a {@linkplain StructLayout struct layout}, whereas a C {@code union} type maps to a {@link UnionLayout union
|
||||
* layout}. When defining a struct or union layout, clients must pay attention to the size and alignment constraint
|
||||
* of the corresponding composite type definition in C. For instance, padding between two struct fields
|
||||
* must be modelled explicitly, by adding an adequately sized {@linkplain PaddingLayout padding layout} member
|
||||
* to the resulting struct layout.
|
||||
* <p>
|
||||
* Finally, pointer types such as {@code int**} and {@code int(*)(size_t*, size_t*)} are modelled as
|
||||
* {@linkplain AddressLayout address layouts}. When the spatial bounds of the pointer type are known statically,
|
||||
* the address layout can be associated with a {@linkplain AddressLayout#targetLayout() target layout}. For instance,
|
||||
* a pointer that is known to point to a C {@code int[2]} array can be modelled as an address layout whose
|
||||
* target layout is a sequence layout whose element count is 2, and whose element type is {@link ValueLayout#JAVA_INT}.
|
||||
* <p>
|
||||
* The following table shows some examples of how C types are modelled in Linux/x64:
|
||||
*
|
||||
* <blockquote><table class="plain">
|
||||
* <caption style="display:none">Mapping C types</caption>
|
||||
* <thead>
|
||||
* <tr>
|
||||
* <th scope="col">C type</th>
|
||||
* <th scope="col">Layout</th>
|
||||
* <th scope="col">Java type</th>
|
||||
* </tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><th scope="row" style="font-weight:normal">{@code bool}</th>
|
||||
* <td style="text-align:center;">{@link ValueLayout#JAVA_BOOLEAN}</td>
|
||||
* <td style="text-align:center;">{@code boolean}</td>
|
||||
* <tr><th scope="row" style="font-weight:normal">{@code char}</th>
|
||||
* <td style="text-align:center;">{@link ValueLayout#JAVA_BYTE}</td>
|
||||
* <td style="text-align:center;">{@code byte}</td>
|
||||
* <tr><th scope="row" style="font-weight:normal">{@code short}</th>
|
||||
* <td style="text-align:center;">{@link ValueLayout#JAVA_SHORT}</td>
|
||||
* <td style="text-align:center;">{@code short}</td>
|
||||
* <tr><th scope="row" style="font-weight:normal">{@code int}</th>
|
||||
* <td style="text-align:center;">{@link ValueLayout#JAVA_INT}</td>
|
||||
* <td style="text-align:center;">{@code int}</td>
|
||||
* <tr><th scope="row" style="font-weight:normal">{@code long}</th>
|
||||
* <td style="text-align:center;">{@link ValueLayout#JAVA_LONG}</td>
|
||||
* <td style="text-align:center;">{@code long}</td>
|
||||
* <tr><th scope="row" style="font-weight:normal">{@code long long}</th>
|
||||
* <td style="text-align:center;">{@link ValueLayout#JAVA_LONG}</td>
|
||||
* <td style="text-align:center;">{@code long}</td>
|
||||
* <tr><th scope="row" style="font-weight:normal">{@code float}</th>
|
||||
* <td style="text-align:center;">{@link ValueLayout#JAVA_FLOAT}</td>
|
||||
* <td style="text-align:center;">{@code float}</td>
|
||||
* <tr><th scope="row" style="font-weight:normal">{@code double}</th>
|
||||
* <td style="text-align:center;">{@link ValueLayout#JAVA_DOUBLE}</td>
|
||||
* <td style="text-align:center;">{@code double}</td>
|
||||
<tr><th scope="row" style="font-weight:normal">{@code size_t}</th>
|
||||
* <td style="text-align:center;">{@link ValueLayout#JAVA_LONG}</td>
|
||||
* <td style="text-align:center;">{@code long}</td>
|
||||
* <tr><th scope="row" style="font-weight:normal">{@code char*}, {@code int**}, {@code struct Point*}</th>
|
||||
* <td style="text-align:center;">{@link ValueLayout#ADDRESS}</td>
|
||||
* <td style="text-align:center;">{@link MemorySegment}</td>
|
||||
* <tr><th scope="row" style="font-weight:normal">{@code int (*ptr)[10]}</th>
|
||||
* <td style="text-align:left;">
|
||||
* <pre>
|
||||
* ValueLayout.ADDRESS.withTargetLayout(
|
||||
* MemoryLayout.sequenceLayout(10,
|
||||
* ValueLayout.JAVA_INT)
|
||||
* );
|
||||
* </pre>
|
||||
* <td style="text-align:center;">{@link MemorySegment}</td>
|
||||
* <tr><th scope="row" style="font-weight:normal"><code>struct Point { int x; long y; };</code></th>
|
||||
* <td style="text-align:left;">
|
||||
* <pre>
|
||||
* MemoryLayout.structLayout(
|
||||
* ValueLayout.JAVA_INT.withName("x"),
|
||||
* MemoryLayout.paddingLayout(32),
|
||||
* ValueLayout.JAVA_LONG.withName("y")
|
||||
* );
|
||||
* </pre>
|
||||
* </td>
|
||||
* <td style="text-align:center;">{@link MemorySegment}</td>
|
||||
* <tr><th scope="row" style="font-weight:normal"><code>union Choice { float a; int b; }</code></th>
|
||||
* <td style="text-align:left;">
|
||||
* <pre>
|
||||
* MemoryLayout.unionLayout(
|
||||
* ValueLayout.JAVA_FLOAT.withName("a"),
|
||||
* ValueLayout.JAVA_INT.withName("b")
|
||||
* );
|
||||
* </pre>
|
||||
* </td>
|
||||
* <td style="text-align:center;">{@link MemorySegment}</td>
|
||||
* </tbody>
|
||||
* </table></blockquote>
|
||||
*
|
||||
* <h3 id="function-pointers">Function pointers</h3>
|
||||
*
|
||||
* Sometimes, it is useful to pass Java code as a function pointer to some native function; this is achieved by using
|
||||
* an {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, Arena, Option...) upcall stub}. To demonstrate this,
|
||||
* let's consider the following function from the C standard library:
|
||||
*
|
||||
* {@snippet lang = c:
|
||||
* void qsort(void *base, size_t nmemb, size_t size,
|
||||
* int (*compar)(const void *, const void *));
|
||||
* }
|
||||
*
|
||||
* The {@code qsort} function can be used to sort the contents of an array, using a custom comparator function which is
|
||||
* passed as a function pointer (the {@code compar} parameter). To be able to call the {@code qsort} function from Java,
|
||||
* we must first create a downcall method handle for it, as follows:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* Linker linker = Linker.nativeLinker();
|
||||
* MethodHandle qsort = linker.downcallHandle(
|
||||
* linker.defaultLookup().find("qsort").get(),
|
||||
* FunctionDescriptor.ofVoid(ADDRESS, JAVA_LONG, JAVA_LONG, ADDRESS)
|
||||
* );
|
||||
* }
|
||||
*
|
||||
* As before, we use {@link ValueLayout#JAVA_LONG} to map the C type {@code size_t} type, and {@link ValueLayout#ADDRESS}
|
||||
* for both the first pointer parameter (the array pointer) and the last parameter (the function pointer).
|
||||
* <p>
|
||||
* To invoke the {@code qsort} downcall handle obtained above, we need a function pointer to be passed as the last
|
||||
* parameter. That is, we need to create a function pointer out of an existing method handle. First, let's write a
|
||||
* Java method that can compare two int elements passed as pointers (i.e. as {@linkplain MemorySegment memory segments}):
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* class Qsort {
|
||||
* static int qsortCompare(MemorySegment elem1, MemorySegmet elem2) {
|
||||
* return Integer.compare(elem1.get(JAVA_INT, 0), elem2.get(JAVA_INT, 0));
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* Now let's create a method handle for the comparator method defined above:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* FunctionDescriptor comparDesc = FunctionDescriptor.of(JAVA_INT,
|
||||
* ADDRESS.withTargetLayout(JAVA_INT),
|
||||
* ADDRESS.withTargetLayout(JAVA_INT));
|
||||
* MethodHandle comparHandle = MethodHandles.lookup()
|
||||
* .findStatic(Qsort.class, "qsortCompare",
|
||||
* comparDesc.toMethodType());
|
||||
* }
|
||||
*
|
||||
* First, we create a function descriptor for the function pointer type. Since we know that the parameters passed to
|
||||
* the comparator method will be pointers to elements of a C {@code int[]} array, we can specify {@link ValueLayout#JAVA_INT}
|
||||
* as the target layout for the address layouts of both parameters. This will allow the comparator method to access
|
||||
* the contents of the array elements to be compared. We then {@linkplain FunctionDescriptor#toMethodType() turn}
|
||||
* that function descriptor into a suitable {@linkplain java.lang.invoke.MethodType method type} which we then use to look up
|
||||
* the comparator method handle. We can now create an upcall stub which points to that method, and pass it, as a function
|
||||
* pointer, to the {@code qsort} downcall handle, as follows:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* try (Arena arena = Arena.ofConfined()) {
|
||||
* MemorySegment comparFunc = linker.upcallStub(comparHandle, comparDesc, arena);
|
||||
* MemorySegment array = session.allocateArray(0, 9, 3, 4, 6, 5, 1, 8, 2, 7);
|
||||
* qsort.invokeExact(array, 10L, 4L, comparFunc);
|
||||
* int[] sorted = array.toArray(JAVA_INT); // [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* This code creates an off-heap array, copies the contents of a Java array into it, and then passes the array to the
|
||||
* {@code qsort} method handle along with the comparator function we obtained from the native linker. After the invocation, the contents
|
||||
* of the off-heap array will be sorted according to our comparator function, written in Java. We then extract a
|
||||
* new Java array from the segment, which contains the sorted elements.
|
||||
*
|
||||
* <h3 id="by-ref">Functions returning pointers</h3>
|
||||
*
|
||||
* When interacting with native functions, it is common for those functions to allocate a region of memory and return
|
||||
* a pointer to that region. Let's consider the following function from the C standard library:
|
||||
*
|
||||
* {@snippet lang = c:
|
||||
* void *malloc(size_t size);
|
||||
* }
|
||||
*
|
||||
* The {@code malloc} function allocates a region of memory of given size,
|
||||
* and returns a pointer to that region of memory, which is later deallocated using another function from
|
||||
* the C standard library:
|
||||
*
|
||||
* {@snippet lang = c:
|
||||
* void free(void *ptr);
|
||||
* }
|
||||
*
|
||||
* The {@code free} function takes a pointer to a region of memory and deallocates that region. In this section we
|
||||
* will show how to interact with these native functions, with the aim of providing a <em>safe</em> allocation
|
||||
* API (the approach outlined below can of course be generalized to allocation functions other than {@code malloc}
|
||||
* and {@code free}).
|
||||
* <p>
|
||||
* First, we need to create the downcall method handles for {@code malloc} and {@code free}, as follows:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* Linker linker = Linker.nativeLinker();
|
||||
*
|
||||
* MethodHandle malloc = linker.downcallHandle(
|
||||
* linker.defaultLookup().find("malloc").get(),
|
||||
* FunctionDescriptor.of(ADDRESS, JAVA_LONG)
|
||||
* );
|
||||
*
|
||||
* MethodHandle free = linker.downcallHandle(
|
||||
* linker.defaultLookup().find("free").get(),
|
||||
* FunctionDescriptor.ofVoid(ADDRESS)
|
||||
* );
|
||||
* }
|
||||
*
|
||||
* When interacting with a native functions returning a pointer (such as {@code malloc}), the Java runtime has no insight
|
||||
* into the size or the lifetime of the returned pointer. Consider the following code:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* MemorySegment segment = (MemorySegment)malloc.invokeExact(100);
|
||||
* }
|
||||
*
|
||||
* The size of the segment returned by the {@code malloc} downcall method handle is
|
||||
* <a href="MemorySegment.html#wrapping-addresses">zero</a>. Moreover, the scope of the
|
||||
* returned segment is a fresh scope that is always alive. To provide safe access to the segment, we must,
|
||||
* unsafely, resize the segment to the desired size (100, in this case). It might also be desirable to
|
||||
* attach the segment to some existing {@linkplain Arena arena}, so that the lifetime of the region of memory
|
||||
* backing the segment can be managed automatically, as for any other native segment created directly from Java code.
|
||||
* Both these operations are accomplished using the restricted {@link MemorySegment#reinterpret(long, Arena, Consumer)}
|
||||
* method, as follows:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* MemorySegment allocateMemory(long byteSize, Arena arena) {
|
||||
* MemorySegment segment = (MemorySegment)malloc.invokeExact(byteSize); // size = 0, scope = always alive
|
||||
* return segment.reinterpret(byteSize, arena, s -> free.invokeExact(s)); // size = byteSize, scope = arena.scope()
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* The {@code allocateMemory} method defined above accepts two parameters: a size and an arena. The method calls the
|
||||
* {@code malloc} downcall method handle, and unsafely reinterprets the returned segment, by giving it a new size
|
||||
* (the size passed to the {@code allocateMemory} method) and a new scope (the scope of the provided arena).
|
||||
* The method also specifies a <em>cleanup action</em> to be executed when the provided arena is closed. Unsurprisingly,
|
||||
* the cleanup action passes the segment to the {@code free} downcall method handle, to deallocate the underlying
|
||||
* region of memory. We can use the {@code allocateMemory} method as follows:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* try (Arena arena = Arena.ofConfined()) {
|
||||
* MemorySegment segment = allocateMemory(100, arena);
|
||||
* } // 'free' called here
|
||||
* }
|
||||
*
|
||||
* Note how the segment obtained from {@code allocateMemory} acts as any other segment managed by the confined arena. More
|
||||
* specifically, the obtained segment has the desired size, can only be accessed by a single thread (the thread which created
|
||||
* the confined arena), and its lifetime is tied to the surrounding <em>try-with-resources</em> block.
|
||||
*
|
||||
* <h3 id="variadic-funcs">Variadic functions</h3>
|
||||
*
|
||||
* Variadic functions (e.g. a C function declared with a trailing ellipses {@code ...} at the end of the formal parameter
|
||||
* list or with an empty formal parameter list) are not supported directly by the native linker. However, it is still possible
|
||||
* to link a variadic function by using a <em>specialized</em> function descriptor, together with a
|
||||
* {@linkplain Linker.Option#firstVariadicArg(int) a linker option} which indicates the position of the first variadic argument
|
||||
* in that specialized descriptor.
|
||||
* <p>
|
||||
* A well-known variadic function is the {@code printf} function, defined in the C standard library:
|
||||
*
|
||||
* {@snippet lang = c:
|
||||
* int printf(const char *format, ...);
|
||||
* }
|
||||
*
|
||||
* This function takes a format string, and a number of additional arguments (the number of such arguments is
|
||||
* dictated by the format string). Consider the following variadic call:
|
||||
*
|
||||
* {@snippet lang = c:
|
||||
* printf("%d plus %d equals %d", 2, 2, 4);
|
||||
* }
|
||||
*
|
||||
* To perform an equivalent call using a downcall method handle we must create a function descriptor which
|
||||
* describes the specialized signature of the C function we want to call. This descriptor must include layouts for any
|
||||
* additional variadic argument we intend to provide. In this case, the specialized signature of the C
|
||||
* function is {@code (char*, int, int, int)} as the format string accepts three integer parameters. Then, we need to use
|
||||
* a linker option to specify the position of the first variadic layout in the provided function descriptor (starting from 0).
|
||||
* In this case, since the first parameter is the format string (a non-variadic argument), the first variadic index
|
||||
* needs to be set to 1, as follows:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* Linker linker = Linker.nativeLinker();
|
||||
* MethodHandle printf = linker.downcallHandle(
|
||||
* linker.defaultLookup().lookup("printf").get(),
|
||||
* FunctionDescriptor.of(JAVA_INT, ADDRESS, JAVA_INT, JAVA_INT, JAVA_INT),
|
||||
* Linker.Option.firstVariadicArg(1) // first int is variadic
|
||||
* );
|
||||
* }
|
||||
*
|
||||
* We can then call the specialized downcall handle as usual:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* try (Arena arena = Arena.ofConfined()) {
|
||||
* int res = (int)printf.invokeExact(arena.allocateUtf8String("%d plus %d equals %d"), 2, 2, 4); //prints "2 plus 2 equals 4"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* <h2 id="safety">Safety considerations</h2>
|
||||
*
|
||||
@ -101,21 +397,7 @@ import java.util.stream.Stream;
|
||||
* contain enough signature information (e.g. arity and types of foreign function parameters). As a consequence,
|
||||
* the linker runtime cannot validate linkage requests. When a client interacts with a downcall method handle obtained
|
||||
* through an invalid linkage request (e.g. by specifying a function descriptor featuring too many argument layouts),
|
||||
* the result of such interaction is unspecified and can lead to JVM crashes. On downcall handle invocation,
|
||||
* the linker runtime guarantees the following for any argument {@code A} of type {@link MemorySegment} whose corresponding
|
||||
* layout is {@link ValueLayout#ADDRESS}:
|
||||
* <ul>
|
||||
* <li>The scope of {@code A} is {@linkplain SegmentScope#isAlive() alive}. Otherwise, the invocation throws
|
||||
* {@link IllegalStateException};</li>
|
||||
* <li>The invocation occurs in a thread {@code T} such that {@code A.scope().isAccessibleBy(T) == true}.
|
||||
* Otherwise, the invocation throws {@link WrongThreadException}; and</li>
|
||||
* <li>The scope of {@code A} is {@linkplain SegmentScope#whileAlive(Runnable) kept alive} during the invocation.</li>
|
||||
*</ul>
|
||||
* A downcall method handle created from a function descriptor whose return layout is an
|
||||
* {@linkplain ValueLayout.OfAddress address layout} returns a native segment associated with
|
||||
* the {@linkplain SegmentScope#global() global scope}. Under normal conditions, the size of the returned segment is {@code 0}.
|
||||
* However, if the return layout is an {@linkplain ValueLayout.OfAddress#asUnbounded() unbounded} address layout,
|
||||
* then the size of the returned segment is {@code Long.MAX_VALUE}.
|
||||
* the result of such interaction is unspecified and can lead to JVM crashes.
|
||||
* <p>
|
||||
* When creating upcall stubs the linker runtime validates the type of the target method handle against the provided
|
||||
* function descriptor and report an error if any mismatch is detected. As for downcalls, JVM crashes might occur,
|
||||
@ -124,12 +406,6 @@ import java.util.stream.Stream;
|
||||
* handle associated with an upcall stub returns a {@linkplain MemorySegment memory segment}, clients must ensure
|
||||
* that this address cannot become invalid after the upcall completes. This can lead to unspecified behavior,
|
||||
* and even JVM crashes, since an upcall is typically executed in the context of a downcall method handle invocation.
|
||||
* <p>
|
||||
* An upcall stub argument whose corresponding layout is an {@linkplain ValueLayout.OfAddress address layout}
|
||||
* is a native segment associated with the {@linkplain SegmentScope#global() global scope}.
|
||||
* Under normal conditions, the size of this segment argument is {@code 0}. However, if the layout associated with
|
||||
* the upcall stub argument is an {@linkplain ValueLayout.OfAddress#asUnbounded() unbounded} address layout,
|
||||
* then the size of the segment argument is {@code Long.MAX_VALUE}.
|
||||
*
|
||||
* @implSpec
|
||||
* Implementations of this interface are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
|
||||
@ -143,31 +419,6 @@ public sealed interface Linker permits AbstractLinker {
|
||||
* Returns a linker for the ABI associated with the underlying native platform. The underlying native platform
|
||||
* is the combination of OS and processor where the Java runtime is currently executing.
|
||||
* <p>
|
||||
* When interacting with the returned linker, clients must describe the signature of a foreign function using a
|
||||
* {@link FunctionDescriptor function descriptor} whose argument and return layouts are specified as follows:
|
||||
* <ul>
|
||||
* <li>Scalar types are modelled by a {@linkplain ValueLayout value layout} instance of a suitable carrier. Example
|
||||
* of scalar types in C are {@code int}, {@code long}, {@code size_t}, etc. The mapping between a scalar type
|
||||
* and its corresponding layout is dependent on the ABI of the returned linker;
|
||||
* <li>Composite types are modelled by a {@linkplain GroupLayout group layout}. Depending on the ABI of the
|
||||
* returned linker, additional {@linkplain MemoryLayout#paddingLayout(long) padding} member layouts might be required to conform
|
||||
* to the size and alignment constraint of a composite type definition in C (e.g. using {@code struct} or {@code union}); and</li>
|
||||
* <li>Pointer types are modelled by a {@linkplain ValueLayout value layout} instance with carrier {@link MemorySegment}.
|
||||
* Examples of pointer types in C are {@code int**} and {@code int(*)(size_t*, size_t*)};</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Any layout not listed above is <em>unsupported</em>; function descriptors containing unsupported layouts
|
||||
* will cause an {@link IllegalArgumentException} to be thrown, when used to create a
|
||||
* {@link #downcallHandle(MemorySegment, FunctionDescriptor, Option...) downcall method handle} or an
|
||||
* {@linkplain #upcallStub(MethodHandle, FunctionDescriptor, SegmentScope) upcall stub}.
|
||||
* <p>
|
||||
* Variadic functions (e.g. a C function declared with a trailing ellipses {@code ...} at the end of the formal parameter
|
||||
* list or with an empty formal parameter list) are not supported directly. However, it is possible to link a
|
||||
* variadic function by using {@linkplain Linker.Option#firstVariadicArg(int) a linker option} to indicate
|
||||
* the start of the list of variadic arguments, together with a specialized function descriptor describing a
|
||||
* given variable arity callsite. Alternatively, where the foreign library allows it, clients might be able to
|
||||
* interact with variadic functions by passing a trailing parameter of type {@link VaList} (e.g. as in {@code vsprintf}).
|
||||
* <p>
|
||||
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
|
||||
* Restricted methods are unsafe, and, if used incorrectly, their use might crash
|
||||
* the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
|
||||
@ -178,7 +429,7 @@ public sealed interface Linker permits AbstractLinker {
|
||||
* linker are the native libraries loaded in the process where the Java runtime is currently executing. For example,
|
||||
* on Linux, these libraries typically include {@code libc}, {@code libm} and {@code libdl}.
|
||||
*
|
||||
* @return a linker for the ABI associated with the OS and processor where the Java runtime is currently executing.
|
||||
* @return a linker for the ABI associated with the underlying native platform.
|
||||
* @throws UnsupportedOperationException if the underlying native platform is not supported.
|
||||
* @throws IllegalCallerException If the caller is in a module that does not have native access enabled.
|
||||
*/
|
||||
@ -189,11 +440,7 @@ public sealed interface Linker permits AbstractLinker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a method handle which can be used to call a foreign function with the given signature and address.
|
||||
* <p>
|
||||
* If the provided method type's return type is {@code MemorySegment}, then the resulting method handle features
|
||||
* an additional prefix parameter, of type {@link SegmentAllocator}, which will be used by the linker to allocate
|
||||
* structs returned by-value.
|
||||
* Creates a method handle which is used to call a foreign function with the given signature and address.
|
||||
* <p>
|
||||
* Calling this method is equivalent to the following code:
|
||||
* {@snippet lang=java :
|
||||
@ -214,17 +461,35 @@ public sealed interface Linker permits AbstractLinker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a method handle which can be used to call a foreign function with the given signature.
|
||||
* The resulting method handle features a prefix parameter (as the first parameter) corresponding to the foreign function
|
||||
* entry point, of type {@link MemorySegment}, which is used to specify the address of the target function
|
||||
* to be called.
|
||||
* Creates a method handle which is used to call a foreign function with the given signature.
|
||||
* <p>
|
||||
* If the provided function descriptor's return layout is a {@link GroupLayout}, then the resulting method handle features an
|
||||
* additional prefix parameter (inserted immediately after the address parameter), of type {@link SegmentAllocator}),
|
||||
* which will be used by the linker to allocate structs returned by-value.
|
||||
* The Java {@linkplain java.lang.invoke.MethodType method type} associated with the returned method handle is
|
||||
* {@linkplain FunctionDescriptor#toMethodType() derived} from the argument and return layouts in the function descriptor,
|
||||
* but features an additional leading parameter of type {@link MemorySegment}, from which the address of the target
|
||||
* foreign function is derived. Moreover, if the function descriptor's return layout is a group layout, the resulting
|
||||
* downcall method handle accepts an additional leading parameter of type {@link SegmentAllocator}, which is used by
|
||||
* the linker runtime to allocate the memory region associated with the struct returned by the downcall method handle.
|
||||
* <p>
|
||||
* The returned method handle will throw an {@link IllegalArgumentException} if the {@link MemorySegment} parameter passed to it is
|
||||
* associated with the {@link MemorySegment#NULL} address, or a {@link NullPointerException} if that parameter is {@code null}.
|
||||
* Upon invoking a downcall method handle, the linker runtime will guarantee the following for any argument
|
||||
* {@code A} of type {@link MemorySegment} whose corresponding layout is an {@linkplain AddressLayout address layout}:
|
||||
* <ul>
|
||||
* <li>{@code A.scope().isAlive() == true}. Otherwise, the invocation throws {@link IllegalStateException};</li>
|
||||
* <li>The invocation occurs in a thread {@code T} such that {@code A.isAccessibleBy(T) == true}.
|
||||
* Otherwise, the invocation throws {@link WrongThreadException}; and</li>
|
||||
* <li>{@code A} is kept alive during the invocation. For instance, if {@code A} has been obtained using a
|
||||
* {@linkplain Arena#ofShared()} shared arena}, any attempt to {@linkplain Arena#close() close}
|
||||
* the shared arena while the downcall method handle is executing will result in an {@link IllegalStateException}.</li>
|
||||
*</ul>
|
||||
* <p>
|
||||
* Moreover, if the provided function descriptor's return layout is an {@linkplain AddressLayout address layout},
|
||||
* invoking the returned method handle will return a native segment associated with
|
||||
* a fresh scope that is always alive. Under normal conditions, the size of the returned segment is {@code 0}.
|
||||
* However, if the function descriptor's return layout has a {@linkplain AddressLayout#targetLayout()} {@code T},
|
||||
* then the size of the returned segment is set to {@code T.byteSize()}.
|
||||
* <p>
|
||||
* The returned method handle will throw an {@link IllegalArgumentException} if the {@link MemorySegment}
|
||||
* representing the target address of the foreign function is the {@link MemorySegment#NULL} address.
|
||||
* The returned method handle will additionally throw {@link NullPointerException} if any argument passed to it is {@code null}.
|
||||
*
|
||||
* @param function the function descriptor of the target function.
|
||||
* @param options any linker options.
|
||||
@ -237,12 +502,19 @@ public sealed interface Linker permits AbstractLinker {
|
||||
|
||||
/**
|
||||
* Creates a stub which can be passed to other foreign functions as a function pointer, associated with the given
|
||||
* scope. Calling such a function pointer from foreign code will result in the execution of the provided
|
||||
* arena. Calling such a function pointer from foreign code will result in the execution of the provided
|
||||
* method handle.
|
||||
* <p>
|
||||
* The returned memory segment's address points to the newly allocated upcall stub, and is associated with
|
||||
* the provided scope. As such, the corresponding upcall stub will be deallocated
|
||||
* when the scope becomes not {@linkplain SegmentScope#isAlive() alive}.
|
||||
* the provided arena. As such, the lifetime of the returned upcall stub segment is controlled by the
|
||||
* provided arena. For instance, if the provided arena is a confined arena, the returned
|
||||
* upcall stub segment will be deallocated when the provided confined arena is {@linkplain Arena#close() closed}.
|
||||
* <p>
|
||||
* An upcall stub argument whose corresponding layout is an {@linkplain AddressLayout address layout}
|
||||
* is a native segment associated with a fresh scope that is always alive.
|
||||
* Under normal conditions, the size of this segment argument is {@code 0}.
|
||||
* However, if the address layout has a {@linkplain AddressLayout#targetLayout()} {@code T}, then the size of the
|
||||
* segment argument is set to {@code T.byteSize()}.
|
||||
* <p>
|
||||
* The target method handle should not throw any exceptions. If the target method handle does throw an exception,
|
||||
* the VM will exit with a non-zero exit code. To avoid the VM aborting due to an uncaught exception, clients
|
||||
@ -252,16 +524,17 @@ public sealed interface Linker permits AbstractLinker {
|
||||
*
|
||||
* @param target the target method handle.
|
||||
* @param function the upcall stub function descriptor.
|
||||
* @param scope the scope associated with the returned upcall stub segment.
|
||||
* @param arena the arena associated with the returned upcall stub segment.
|
||||
* @param options any linker options.
|
||||
* @return a zero-length segment whose address is the address of the upcall stub.
|
||||
* @throws IllegalArgumentException if the provided function descriptor is not supported by this linker.
|
||||
* @throws IllegalArgumentException if it is determined that the target method handle can throw an exception, or if the target method handle
|
||||
* has a type that does not match the upcall stub <a href="Linker.html#upcall-stubs"><em>inferred type</em></a>.
|
||||
* @throws IllegalStateException if {@code scope} is not {@linkplain SegmentScope#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread {@code T},
|
||||
* such that {@code scope.isAccessibleBy(T) == false}.
|
||||
* @throws IllegalStateException if {@code arena.scope().isAlive() == false}
|
||||
* @throws WrongThreadException if {@code arena} is a confined arena, and this method is called from a
|
||||
* thread {@code T}, other than the arena's owner thread.
|
||||
*/
|
||||
MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, SegmentScope scope);
|
||||
MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, Arena arena, Linker.Option... options);
|
||||
|
||||
/**
|
||||
* Returns a symbol lookup for symbols in a set of commonly used libraries.
|
||||
@ -285,8 +558,7 @@ public sealed interface Linker permits AbstractLinker {
|
||||
*/
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
||||
sealed interface Option
|
||||
permits LinkerOptions.LinkerOptionImpl,
|
||||
Option.CaptureCallState {
|
||||
permits LinkerOptions.LinkerOptionImpl {
|
||||
|
||||
/**
|
||||
* {@return a linker option used to denote the index of the first variadic argument layout in a
|
||||
@ -302,70 +574,91 @@ public sealed interface Linker permits AbstractLinker {
|
||||
* calling a foreign function associated with a downcall method handle,
|
||||
* before it can be overwritten by the Java runtime, or read through conventional means}
|
||||
* <p>
|
||||
* A downcall method handle linked with this option will feature an additional {@link MemorySegment}
|
||||
* parameter directly following the target address, and optional {@link SegmentAllocator} parameters.
|
||||
* This memory segment must be a native segment into which the captured state is written.
|
||||
*
|
||||
* @param capturedState the names of the values to save.
|
||||
* @see CaptureCallState#supported()
|
||||
*/
|
||||
static CaptureCallState captureCallState(String... capturedState) {
|
||||
Set<CapturableState> set = Stream.of(capturedState)
|
||||
.map(CapturableState::forName)
|
||||
.collect(Collectors.toSet());
|
||||
return new LinkerOptions.CaptureCallStateImpl(set);
|
||||
}
|
||||
|
||||
/**
|
||||
* A linker option for saving portions of the execution state immediately
|
||||
* after calling a foreign function associated with a downcall method handle,
|
||||
* before it can be overwritten by the runtime, or read through conventional means.
|
||||
* <p>
|
||||
* Execution state is captured by a downcall method handle on invocation, by writing it
|
||||
* to a native segment provided by the user to the downcall method handle.
|
||||
* For this purpose, a downcall method handle linked with the {@link #captureCallState(String[])}
|
||||
* For this purpose, a downcall method handle linked with this
|
||||
* option will feature an additional {@link MemorySegment} parameter directly
|
||||
* following the target address, and optional {@link SegmentAllocator} parameters.
|
||||
* This parameter represents the native segment into which the captured state is written.
|
||||
* This parameter, called the 'capture state segment', represents the native segment into which
|
||||
* the captured state is written.
|
||||
* <p>
|
||||
* The native segment should have the layout {@linkplain CaptureCallState#layout associated}
|
||||
* with the particular {@code CaptureCallState} instance used to link the downcall handle.
|
||||
* The capture state segment should have the layout returned by {@linkplain #captureStateLayout}.
|
||||
* This layout is a struct layout which has a named field for each captured value.
|
||||
* <p>
|
||||
* Captured state can be retrieved from this native segment by constructing var handles
|
||||
* from the {@linkplain #layout layout} associated with the {@code CaptureCallState} instance.
|
||||
* Captured state can be retrieved from the capture state segment by constructing var handles
|
||||
* from the {@linkplain #captureStateLayout capture state layout}.
|
||||
* <p>
|
||||
* The following example demonstrates the use of this linker option:
|
||||
* {@snippet lang = "java":
|
||||
* MemorySegment targetAddress = ...
|
||||
* CaptureCallState ccs = Linker.Option.captureCallState("errno");
|
||||
* Linker.Option ccs = Linker.Option.captureCallState("errno");
|
||||
* MethodHandle handle = Linker.nativeLinker().downcallHandle(targetAddress, FunctionDescriptor.ofVoid(), ccs);
|
||||
*
|
||||
* VarHandle errnoHandle = ccs.layout().varHandle(PathElement.groupElement("errno"));
|
||||
* try (Arena arena = Arena.openConfined()) {
|
||||
* MemorySegment capturedState = arena.allocate(ccs.layout());
|
||||
* StructLayout capturedStateLayout = Linker.Option.capturedStateLayout();
|
||||
* VarHandle errnoHandle = capturedStateLayout.varHandle(PathElement.groupElement("errno"));
|
||||
* try (Arena arena = Arena.ofConfined()) {
|
||||
* MemorySegment capturedState = arena.allocate(capturedStateLayout);
|
||||
* handle.invoke(capturedState);
|
||||
* int errno = errnoHandle.get(capturedState);
|
||||
* // use errno
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @param capturedState the names of the values to save.
|
||||
* @throws IllegalArgumentException if at least one of the provided {@code capturedState} names
|
||||
* is unsupported on the current platform.
|
||||
* @see #captureStateLayout()
|
||||
*/
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
||||
sealed interface CaptureCallState extends Option
|
||||
permits LinkerOptions.CaptureCallStateImpl {
|
||||
/**
|
||||
* {@return A struct layout that represents the layout of the native segment passed
|
||||
* to a downcall handle linked with this {@code CapturedCallState} instance}
|
||||
*/
|
||||
StructLayout layout();
|
||||
static Option captureCallState(String... capturedState) {
|
||||
Set<CapturableState> set = Stream.of(Objects.requireNonNull(capturedState))
|
||||
.map(Objects::requireNonNull)
|
||||
.map(CapturableState::forName)
|
||||
.collect(Collectors.toSet());
|
||||
return new LinkerOptions.CaptureCallState(set);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the names of the state that can be capture by this implementation}
|
||||
*/
|
||||
static Set<String> supported() {
|
||||
return Arrays.stream(CapturableState.values())
|
||||
.map(CapturableState::stateName)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
/**
|
||||
* {@return A struct layout that represents the layout of the capture state segment that is passed
|
||||
* to a downcall handle linked with {@link #captureCallState(String...)}}.
|
||||
* <p>
|
||||
* The capture state layout is <em>platform dependent</em> but is guaranteed to be
|
||||
* a {@linkplain StructLayout struct layout} containing only {@linkplain ValueLayout value layouts}
|
||||
* and possibly {@linkplain PaddingLayout padding layouts}.
|
||||
* As an example, on Windows, the returned layout might contain three value layouts named:
|
||||
* <ul>
|
||||
* <li>GetLastError</li>
|
||||
* <li>WSAGetLastError</li>
|
||||
* <li>errno</li>
|
||||
* </ul>
|
||||
* The following snipet shows how to obtain the names of the supported captured value layouts:
|
||||
* {@snippet lang = java:
|
||||
* String capturedNames = Linker.Option.captureStateLayout().memberLayouts().stream()
|
||||
* .map(MemoryLayout::name)
|
||||
* .flatMap(Optional::stream)
|
||||
* .map(Objects::toString)
|
||||
* .collect(Collectors.joining(", "));
|
||||
* }
|
||||
*
|
||||
* @see #captureCallState(String...)
|
||||
*/
|
||||
static StructLayout captureStateLayout() {
|
||||
return CapturableState.LAYOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return A linker option used to mark a foreign function as <em>trivial</em>}
|
||||
* <p>
|
||||
* A trivial function is a function that has an extremely short running time
|
||||
* in all cases (similar to calling an empty function), and does not call back into Java (e.g. using an upcall stub).
|
||||
* <p>
|
||||
* Using this linker option is a hint which some implementations may use to apply
|
||||
* optimizations that are only valid for trivial functions.
|
||||
* <p>
|
||||
* Using this linker option when linking non trivial functions is likely to have adverse effects,
|
||||
* such as loss of performance, or JVM crashes.
|
||||
*/
|
||||
static Option isTrivial() {
|
||||
return LinkerOptions.IsTrivial.INSTANCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 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
|
||||
@ -28,7 +28,6 @@ package java.lang.foreign;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
@ -36,6 +35,7 @@ import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.foreign.LayoutPath;
|
||||
import jdk.internal.foreign.LayoutPath.PathElementImpl.PathKind;
|
||||
import jdk.internal.foreign.Utils;
|
||||
@ -44,7 +44,6 @@ import jdk.internal.foreign.layout.PaddingLayoutImpl;
|
||||
import jdk.internal.foreign.layout.SequenceLayoutImpl;
|
||||
import jdk.internal.foreign.layout.StructLayoutImpl;
|
||||
import jdk.internal.foreign.layout.UnionLayoutImpl;
|
||||
import jdk.internal.foreign.layout.ValueLayouts;
|
||||
import jdk.internal.javac.PreviewFeature;
|
||||
|
||||
/**
|
||||
@ -200,6 +199,17 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
||||
*/
|
||||
MemoryLayout withName(String name);
|
||||
|
||||
/**
|
||||
* Returns a memory layout of the same type with the same size and alignment constraint as this layout,
|
||||
* but without a name.
|
||||
* <p>
|
||||
* This can be useful to compare two layouts that have different names, but are otherwise equal.
|
||||
*
|
||||
* @return a memory layout without a name.
|
||||
* @see MemoryLayout#name()
|
||||
*/
|
||||
MemoryLayout withoutName();
|
||||
|
||||
/**
|
||||
* Returns the alignment constraint associated with this layout, expressed in bits. Layout alignment defines a power
|
||||
* of two {@code A} which is the bit-wise alignment of the layout. If {@code A <= 8} then {@code A/8} is the number of
|
||||
@ -235,10 +245,7 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
||||
* @return the layout alignment constraint, in bytes.
|
||||
* @throws UnsupportedOperationException if {@code bitAlignment()} is not a multiple of 8.
|
||||
*/
|
||||
default long byteAlignment() {
|
||||
return Utils.bitsToBytesOrThrow(bitAlignment(),
|
||||
() -> new UnsupportedOperationException("Cannot compute byte alignment; bit alignment is not a multiple of 8"));
|
||||
}
|
||||
long byteAlignment();
|
||||
|
||||
/**
|
||||
* Returns a memory layout of the same type with the same size and name as this layout,
|
||||
@ -259,12 +266,14 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
||||
* @throws IllegalArgumentException if the layout path does not select any layout nested in this layout, or if the
|
||||
* layout path contains one or more path elements that select multiple sequence element indices
|
||||
* (see {@link PathElement#sequenceElement()} and {@link PathElement#sequenceElement(long, long)}).
|
||||
* @throws IllegalArgumentException if the layout path contains one or more dereference path elements
|
||||
* (see {@link PathElement#dereferenceElement()}).
|
||||
* @throws NullPointerException if either {@code elements == null}, or if any of the elements
|
||||
* in {@code elements} is {@code null}.
|
||||
*/
|
||||
default long bitOffset(PathElement... elements) {
|
||||
return computePathOp(LayoutPath.rootPath(this), LayoutPath::offset,
|
||||
EnumSet.of(PathKind.SEQUENCE_ELEMENT, PathKind.SEQUENCE_RANGE), elements);
|
||||
EnumSet.of(PathKind.SEQUENCE_ELEMENT, PathKind.SEQUENCE_RANGE, PathKind.DEREF_ELEMENT), elements);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -293,10 +302,12 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
||||
* specified by the given layout path elements, when supplied with the missing sequence element indices.
|
||||
* @throws IllegalArgumentException if the layout path contains one or more path elements that select
|
||||
* multiple sequence element indices (see {@link PathElement#sequenceElement(long, long)}).
|
||||
* @throws IllegalArgumentException if the layout path contains one or more dereference path elements
|
||||
* (see {@link PathElement#dereferenceElement()}).
|
||||
*/
|
||||
default MethodHandle bitOffsetHandle(PathElement... elements) {
|
||||
return computePathOp(LayoutPath.rootPath(this), LayoutPath::offsetHandle,
|
||||
EnumSet.of(PathKind.SEQUENCE_RANGE), elements);
|
||||
EnumSet.of(PathKind.SEQUENCE_RANGE, PathKind.DEREF_ELEMENT), elements);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -308,12 +319,14 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
||||
* @throws IllegalArgumentException if the layout path does not select any layout nested in this layout, or if the
|
||||
* layout path contains one or more path elements that select multiple sequence element indices
|
||||
* (see {@link PathElement#sequenceElement()} and {@link PathElement#sequenceElement(long, long)}).
|
||||
* @throws IllegalArgumentException if the layout path contains one or more dereference path elements
|
||||
* (see {@link PathElement#dereferenceElement()}).
|
||||
* @throws UnsupportedOperationException if {@code bitOffset(elements)} is not a multiple of 8.
|
||||
* @throws NullPointerException if either {@code elements == null}, or if any of the elements
|
||||
* in {@code elements} is {@code null}.
|
||||
*/
|
||||
default long byteOffset(PathElement... elements) {
|
||||
return Utils.bitsToBytesOrThrow(bitOffset(elements), Utils.BITS_TO_BYTES_THROW_OFFSET);
|
||||
return Utils.bitsToBytes(bitOffset(elements));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -346,10 +359,12 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
||||
* specified by the given layout path elements, when supplied with the missing sequence element indices.
|
||||
* @throws IllegalArgumentException if the layout path contains one or more path elements that select
|
||||
* multiple sequence element indices (see {@link PathElement#sequenceElement(long, long)}).
|
||||
* @throws IllegalArgumentException if the layout path contains one or more dereference path elements
|
||||
* (see {@link PathElement#dereferenceElement()}).
|
||||
*/
|
||||
default MethodHandle byteOffsetHandle(PathElement... elements) {
|
||||
MethodHandle mh = bitOffsetHandle(elements);
|
||||
mh = MethodHandles.filterReturnValue(mh, Utils.MH_BITS_TO_BYTES_OR_THROW_FOR_OFFSET);
|
||||
mh = MethodHandles.filterReturnValue(mh, Utils.BITS_TO_BYTES);
|
||||
return mh;
|
||||
}
|
||||
|
||||
@ -379,6 +394,28 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
||||
* <p>
|
||||
* Additionally, the provided dynamic values must conform to some bound which is derived from the layout path, that is,
|
||||
* {@code 0 <= x_i < b_i}, where {@code 1 <= i <= n}, or {@link IndexOutOfBoundsException} is thrown.
|
||||
* <p>
|
||||
* Multiple paths can be chained, by using {@linkplain PathElement#dereferenceElement() dereference path elements}.
|
||||
* A dereference path element allows to obtain a native memory segment whose base address is the address obtained
|
||||
* by following the layout path elements immediately preceding the dereference path element. In other words,
|
||||
* if a layout path contains one or more dereference path elements, the final address accessed by the returned
|
||||
* var handle can be computed as follows:
|
||||
*
|
||||
* <blockquote><pre>{@code
|
||||
* address_1 = base(segment) + offset_1
|
||||
* address_2 = base(segment_1) + offset_2
|
||||
* ...
|
||||
* address_k = base(segment_k-1) + offset_k
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* where {@code k} is the number of dereference path elements in a layout path, {@code segment} is the input segment,
|
||||
* {@code segment_1}, ... {@code segment_k-1} are the segments obtained by dereferencing the address associated with
|
||||
* a given dereference path element (e.g. {@code segment_1} is a native segment whose base address is {@code address_1}),
|
||||
* and {@code offset_1}, {@code offset_2}, ... {@code offset_k} are the offsets computed by evaluating
|
||||
* the path elements after a given dereference operation (these offsets are obtained using the computation described
|
||||
* above). In these more complex access operations, all memory accesses immediately preceding a dereference operation
|
||||
* (e.g. those at addresses {@code address_1}, {@code address_2}, ..., {@code address_k-1} are performed using the
|
||||
* {@link VarHandle.AccessMode#GET} access mode.
|
||||
*
|
||||
* @apiNote the resulting var handle will feature an additional {@code long} access coordinate for every
|
||||
* unspecified sequence access component contained in this layout path. Moreover, the resulting var handle
|
||||
@ -388,6 +425,8 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
||||
* @return a var handle which can be used to access a memory segment at the (possibly nested) layout selected by the layout path in {@code elements}.
|
||||
* @throws UnsupportedOperationException if the layout path has one or more elements with incompatible alignment constraint.
|
||||
* @throws IllegalArgumentException if the layout path in {@code elements} does not select a value layout (see {@link ValueLayout}).
|
||||
* @throws IllegalArgumentException if the layout path in {@code elements} contains a {@linkplain PathElement#dereferenceElement()
|
||||
* dereference path element} for an address layout that has no {@linkplain AddressLayout#targetLayout() target layout}.
|
||||
* @see MethodHandles#memorySegmentViewVarHandle(ValueLayout)
|
||||
*/
|
||||
default VarHandle varHandle(PathElement... elements) {
|
||||
@ -432,6 +471,8 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
||||
* @param elements the layout path elements.
|
||||
* @return a method handle which can be used to create a slice of the selected layout element, given a segment.
|
||||
* @throws UnsupportedOperationException if the size of the selected layout in bits is not a multiple of 8.
|
||||
* @throws IllegalArgumentException if the layout path contains one or more dereference path elements
|
||||
* (see {@link PathElement#dereferenceElement()}).
|
||||
*/
|
||||
default MethodHandle sliceHandle(PathElement... elements) {
|
||||
return computePathOp(LayoutPath.rootPath(this), LayoutPath::sliceHandle,
|
||||
@ -446,10 +487,12 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
||||
* @throws IllegalArgumentException if the layout path does not select any layout nested in this layout,
|
||||
* or if the layout path contains one or more path elements that select one or more sequence element indices
|
||||
* (see {@link PathElement#sequenceElement(long)} and {@link PathElement#sequenceElement(long, long)}).
|
||||
* @throws IllegalArgumentException if the layout path contains one or more dereference path elements
|
||||
* (see {@link PathElement#dereferenceElement()}).
|
||||
*/
|
||||
default MemoryLayout select(PathElement... elements) {
|
||||
return computePathOp(LayoutPath.rootPath(this), LayoutPath::layout,
|
||||
EnumSet.of(PathKind.SEQUENCE_ELEMENT_INDEX, PathKind.SEQUENCE_RANGE), elements);
|
||||
EnumSet.of(PathKind.SEQUENCE_ELEMENT_INDEX, PathKind.SEQUENCE_RANGE, PathKind.DEREF_ELEMENT), elements);
|
||||
}
|
||||
|
||||
private static <Z> Z computePathOp(LayoutPath path, Function<LayoutPath, Z> finalizer,
|
||||
@ -489,6 +532,7 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
||||
*
|
||||
* @implSpec in case multiple group elements with a matching name exist, the path element returned by this
|
||||
* method will select the first one; that is, the group element with the lowest offset from current path is selected.
|
||||
* In such cases, using {@link #groupElement(long)} might be preferable.
|
||||
*
|
||||
* @param name the name of the group element to be selected.
|
||||
* @return a path element which selects the group element with the given name.
|
||||
@ -499,6 +543,23 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
||||
path -> path.groupElement(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a path element which selects a member layout with the given index in a group layout.
|
||||
* The path element returned by this method does not alter the number of free dimensions of any path
|
||||
* that is combined with such element.
|
||||
*
|
||||
* @param index the index of the group element to be selected.
|
||||
* @return a path element which selects the group element with the given index.
|
||||
* @throws IllegalArgumentException if {@code index < 0}.
|
||||
*/
|
||||
static PathElement groupElement(long index) {
|
||||
if (index < 0) {
|
||||
throw new IllegalArgumentException("Index < 0");
|
||||
}
|
||||
return new LayoutPath.PathElementImpl(PathKind.GROUP_ELEMENT,
|
||||
path -> path.groupElement(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a path element which selects the element layout at the specified position in a sequence layout.
|
||||
* The path element returned by this method does not alter the number of free dimensions of any path
|
||||
@ -578,6 +639,21 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
||||
return new LayoutPath.PathElementImpl(PathKind.SEQUENCE_ELEMENT,
|
||||
LayoutPath::sequenceElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a path element which dereferences an address layout as its
|
||||
* {@linkplain AddressLayout#targetLayout() target layout} (where set).
|
||||
* The path element returned by this method does not alter the number of free dimensions of any path
|
||||
* that is combined with such element. Using this path layout to dereference an address layout
|
||||
* that has no target layout results in an {@link IllegalArgumentException} (e.g. when
|
||||
* a var handle is {@linkplain #varHandle(PathElement...) obtained}).
|
||||
*
|
||||
* @return a path element which dereferences an address layout.
|
||||
*/
|
||||
static PathElement dereferenceElement() {
|
||||
return new LayoutPath.PathElementImpl(PathKind.DEREF_ELEMENT,
|
||||
LayoutPath::derefElement);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -611,60 +687,14 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
||||
String toString();
|
||||
|
||||
/**
|
||||
* Creates a padding layout with the given size.
|
||||
* Creates a padding layout with the given bitSize and a bit-alignment of eight.
|
||||
*
|
||||
* @param size the padding size in bits.
|
||||
* @param bitSize the padding size in bits.
|
||||
* @return the new selector layout.
|
||||
* @throws IllegalArgumentException if {@code size <= 0}.
|
||||
* @throws IllegalArgumentException if {@code bitSize <= 0} or {@code bitSize % 8 != 0}
|
||||
*/
|
||||
static PaddingLayout paddingLayout(long size) {
|
||||
MemoryLayoutUtil.checkSize(size);
|
||||
return PaddingLayoutImpl.of(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a value layout of given Java carrier and byte order. The type of resulting value layout is determined
|
||||
* by the carrier provided:
|
||||
* <ul>
|
||||
* <li>{@link ValueLayout.OfBoolean}, for {@code boolean.class}</li>
|
||||
* <li>{@link ValueLayout.OfByte}, for {@code byte.class}</li>
|
||||
* <li>{@link ValueLayout.OfShort}, for {@code short.class}</li>
|
||||
* <li>{@link ValueLayout.OfChar}, for {@code char.class}</li>
|
||||
* <li>{@link ValueLayout.OfInt}, for {@code int.class}</li>
|
||||
* <li>{@link ValueLayout.OfFloat}, for {@code float.class}</li>
|
||||
* <li>{@link ValueLayout.OfLong}, for {@code long.class}</li>
|
||||
* <li>{@link ValueLayout.OfDouble}, for {@code double.class}</li>
|
||||
* <li>{@link ValueLayout.OfAddress}, for {@code MemorySegment.class}</li>
|
||||
* </ul>
|
||||
* @param carrier the value layout carrier.
|
||||
* @param order the value layout's byte order.
|
||||
* @return a value layout with the given Java carrier and byte-order.
|
||||
* @throws IllegalArgumentException if the carrier type is not supported.
|
||||
*/
|
||||
static ValueLayout valueLayout(Class<?> carrier, ByteOrder order) {
|
||||
Objects.requireNonNull(carrier);
|
||||
Objects.requireNonNull(order);
|
||||
if (carrier == boolean.class) {
|
||||
return ValueLayouts.OfBooleanImpl.of(order);
|
||||
} else if (carrier == char.class) {
|
||||
return ValueLayouts.OfCharImpl.of(order);
|
||||
} else if (carrier == byte.class) {
|
||||
return ValueLayouts.OfByteImpl.of(order);
|
||||
} else if (carrier == short.class) {
|
||||
return ValueLayouts.OfShortImpl.of(order);
|
||||
} else if (carrier == int.class) {
|
||||
return ValueLayouts.OfIntImpl.of(order);
|
||||
} else if (carrier == float.class) {
|
||||
return ValueLayouts.OfFloatImpl.of(order);
|
||||
} else if (carrier == long.class) {
|
||||
return ValueLayouts.OfLongImpl.of(order);
|
||||
} else if (carrier == double.class) {
|
||||
return ValueLayouts.OfDoubleImpl.of(order);
|
||||
} else if (carrier == MemorySegment.class) {
|
||||
return ValueLayouts.OfAddressImpl.of(order);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported carrier: " + carrier.getName());
|
||||
}
|
||||
static PaddingLayout paddingLayout(long bitSize) {
|
||||
return PaddingLayoutImpl.of(MemoryLayoutUtil.requireBitSizeValid(bitSize, false));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -674,10 +704,12 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
||||
* @param elementLayout the sequence element layout.
|
||||
* @return the new sequence layout with the given element layout and size.
|
||||
* @throws IllegalArgumentException if {@code elementCount } is negative.
|
||||
* @throws IllegalArgumentException if {@code elementLayout.bitAlignment() > elementLayout.bitSize()}.
|
||||
*/
|
||||
static SequenceLayout sequenceLayout(long elementCount, MemoryLayout elementLayout) {
|
||||
MemoryLayoutUtil.checkSize(elementCount, true);
|
||||
MemoryLayoutUtil.requireNonNegative(elementCount);
|
||||
Objects.requireNonNull(elementLayout);
|
||||
Utils.checkElementAlignment(elementLayout, "Element layout alignment greater than its size");
|
||||
return wrapOverflow(() ->
|
||||
SequenceLayoutImpl.of(elementCount, elementLayout));
|
||||
}
|
||||
@ -693,6 +725,7 @@ public sealed interface MemoryLayout permits SequenceLayout, GroupLayout, Paddin
|
||||
*
|
||||
* @param elementLayout the sequence element layout.
|
||||
* @return a new sequence layout with the given element layout and maximum element count.
|
||||
* @throws IllegalArgumentException if {@code elementLayout.bitAlignment() > elementLayout.bitSize()}.
|
||||
*/
|
||||
static SequenceLayout sequenceLayout(MemoryLayout elementLayout) {
|
||||
Objects.requireNonNull(elementLayout);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -50,5 +50,12 @@ public sealed interface PaddingLayout extends MemoryLayout permits PaddingLayout
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
PaddingLayout withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
PaddingLayout withBitAlignment(long bitAlignment);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 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
|
||||
@ -32,7 +32,6 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import jdk.internal.foreign.AbstractMemorySegmentImpl;
|
||||
import jdk.internal.foreign.MemorySessionImpl;
|
||||
import jdk.internal.foreign.SlicingAllocator;
|
||||
import jdk.internal.foreign.Utils;
|
||||
import jdk.internal.javac.PreviewFeature;
|
||||
@ -46,8 +45,6 @@ import jdk.internal.javac.PreviewFeature;
|
||||
* <p>
|
||||
* This interface also defines factories for commonly used allocators:
|
||||
* <ul>
|
||||
* <li>{@link #nativeAllocator(SegmentScope)} obtains a simple allocator which can
|
||||
* be used to allocate native segments;</li>
|
||||
* <li>{@link #slicingAllocator(MemorySegment)} obtains an efficient slicing allocator, where memory
|
||||
* is allocated by repeatedly slicing the provided memory segment;</li>
|
||||
* <li>{@link #prefixAllocator(MemorySegment)} obtains an allocator which wraps a segment (either on-heap or off-heap)
|
||||
@ -201,7 +198,7 @@ public interface SegmentAllocator {
|
||||
* @param value the value to be set on the newly allocated memory block.
|
||||
* @return a segment for the newly allocated memory block.
|
||||
*/
|
||||
default MemorySegment allocate(ValueLayout.OfAddress layout, MemorySegment value) {
|
||||
default MemorySegment allocate(AddressLayout layout, MemorySegment value) {
|
||||
Objects.requireNonNull(value);
|
||||
Objects.requireNonNull(layout);
|
||||
MemorySegment segment = allocate(layout);
|
||||
@ -363,8 +360,8 @@ public interface SegmentAllocator {
|
||||
|
||||
/**
|
||||
* Returns a segment allocator which responds to allocation requests by recycling a single segment. Each
|
||||
* new allocation request will return a new slice starting at the segment offset {@code 0} (alignment
|
||||
* constraints are ignored by this allocator), hence the name <em>prefix allocator</em>.
|
||||
* new allocation request will return a new slice starting at the segment offset {@code 0}, hence the name
|
||||
* <em>prefix allocator</em>.
|
||||
* Equivalent to (but likely more efficient than) the following code:
|
||||
* {@snippet lang=java :
|
||||
* MemorySegment segment = ...
|
||||
@ -384,31 +381,4 @@ public interface SegmentAllocator {
|
||||
static SegmentAllocator prefixAllocator(MemorySegment segment) {
|
||||
return (AbstractMemorySegmentImpl)Objects.requireNonNull(segment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple allocator used to allocate native segments. The returned allocator responds to an allocation request by
|
||||
* returning a native segment backed by a fresh off-heap region of memory, with given byte size and alignment constraint.
|
||||
* <p>
|
||||
* Each native segment obtained by the returned allocator is associated with the provided scope. As such,
|
||||
* the off-heap region which backs the returned segment is freed when the scope becomes not
|
||||
* {@linkplain SegmentScope#isAlive() alive}.
|
||||
* <p>
|
||||
* The {@link MemorySegment#address()} of the native segments obtained by the returned allocator is the starting address of
|
||||
* the newly allocated off-heap memory region backing the segment. Moreover, the {@linkplain MemorySegment#address() address}
|
||||
* of the native segment will be aligned according the provided alignment constraint.
|
||||
* <p>
|
||||
* The off-heap region of memory backing a native segment obtained by the returned allocator is initialized to zero.
|
||||
* <p>
|
||||
* This is equivalent to the following code:
|
||||
* {@snippet lang = java:
|
||||
* SegmentAllocator nativeAllocator = (byteSize, byteAlignment) ->
|
||||
* MemorySegment.allocateNative(byteSize, byteAlignment, scope);
|
||||
* }
|
||||
* @param scope the scope associated with the segments returned by the native allocator.
|
||||
* @return a simple allocator used to allocate native segments.
|
||||
*/
|
||||
static SegmentAllocator nativeAllocator(SegmentScope scope) {
|
||||
Objects.requireNonNull(scope);
|
||||
return (MemorySessionImpl)scope;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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 java.lang.foreign;
|
||||
|
||||
import jdk.internal.foreign.MemorySessionImpl;
|
||||
import jdk.internal.javac.PreviewFeature;
|
||||
import jdk.internal.ref.CleanerFactory;
|
||||
|
||||
/**
|
||||
* A segment scope controls access to memory segments.
|
||||
* <p>
|
||||
* A memory segment can only be accessed while its scope is {@linkplain #isAlive() alive}. Moreover,
|
||||
* depending on how the segment scope has been obtained, access might additionally be
|
||||
* <a href="Arena.html#thread-confinement">restricted to specific threads</a>.
|
||||
* <p>
|
||||
* The simplest segment scope is the {@linkplain SegmentScope#global() global scope}. The global scope
|
||||
* is always alive. As a result, segments associated with the global scope are always accessible and their backing
|
||||
* regions of memory are never deallocated. Moreover, memory segments associated with the global scope
|
||||
* can be {@linkplain #isAccessibleBy(Thread) accessed} from any thread.
|
||||
* {@snippet lang = java:
|
||||
* MemorySegment segment = MemorySegment.allocateNative(100, SegmentScope.global());
|
||||
* ...
|
||||
* // segment is never deallocated!
|
||||
*}
|
||||
* <p>
|
||||
* Alternatively, clients can obtain an {@linkplain SegmentScope#auto() automatic scope}, that is a segment
|
||||
* scope that is managed, automatically, by the garbage collector. The regions of memory backing memory segments associated
|
||||
* with an automatic scope are deallocated at some unspecified time <em>after</em> they become
|
||||
* <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>, as shown below:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* MemorySegment segment = MemorySegment.allocateNative(100, SegmentScope.auto());
|
||||
* ...
|
||||
* segment = null; // the segment region becomes available for deallocation after this point
|
||||
*}
|
||||
* Memory segments associated with an automatic scope can also be {@linkplain #isAccessibleBy(Thread) accessed} from any thread.
|
||||
* <p>
|
||||
* Finally, clients can obtain a segment scope from an existing {@linkplain Arena arena}, the arena scope. The regions of memory
|
||||
* backing memory segments associated with an arena scope are deallocated when the arena is {@linkplain Arena#close() closed}.
|
||||
* When this happens, the arena scope becomes not {@linkplain #isAlive() alive} and subsequent access operations on segments
|
||||
* associated with the arena scope will fail {@link IllegalStateException}.
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* MemorySegment segment = null;
|
||||
* try (Arena arena = Arena.openConfined()) {
|
||||
* segment = MemorySegment.allocateNative(100, arena.scope());
|
||||
* ...
|
||||
* } // segment region deallocated here
|
||||
* segment.get(ValueLayout.JAVA_BYTE, 0); // throws IllegalStateException
|
||||
* }
|
||||
*
|
||||
* Which threads can {@link #isAccessibleBy(Thread) access} memory segments associated with an arena scope depends
|
||||
* on the arena kind. For instance, segments associated with the scope of a {@linkplain Arena#openConfined() confined arena}
|
||||
* can only be accessed by the thread that created the arena. Conversely, segments associated with the scope of
|
||||
* {@linkplain Arena#openConfined() shared arena} can be accessed by any thread.
|
||||
*
|
||||
* @implSpec
|
||||
* Implementations of this interface are thread-safe.
|
||||
*
|
||||
* @see Arena
|
||||
* @see MemorySegment
|
||||
*
|
||||
* @since 20
|
||||
*/
|
||||
@PreviewFeature(feature =PreviewFeature.Feature.FOREIGN)
|
||||
sealed public interface SegmentScope permits MemorySessionImpl {
|
||||
|
||||
/**
|
||||
* Creates a new scope that is managed, automatically, by the garbage collector.
|
||||
* Segments associated with the returned scope can be
|
||||
* {@linkplain SegmentScope#isAccessibleBy(Thread) accessed} by any thread.
|
||||
*
|
||||
* @return a new scope that is managed, automatically, by the garbage collector.
|
||||
*/
|
||||
static SegmentScope auto() {
|
||||
return MemorySessionImpl.createImplicit(CleanerFactory.cleaner());
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the global scope. Segments associated with the global scope can be
|
||||
* {@linkplain SegmentScope#isAccessibleBy(Thread) accessed} by any thread.
|
||||
*
|
||||
* @return the global scope.
|
||||
*/
|
||||
static SegmentScope global() {
|
||||
return MemorySessionImpl.GLOBAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return {@code true}, if this scope is alive}
|
||||
*/
|
||||
boolean isAlive();
|
||||
|
||||
/**
|
||||
* {@return {@code true} if the provided thread can access and/or associate segments with this scope}
|
||||
* @param thread the thread to be tested.
|
||||
*/
|
||||
boolean isAccessibleBy(Thread thread);
|
||||
|
||||
/**
|
||||
* Runs a critical action while this scope is kept alive.
|
||||
* @param action the action to be run.
|
||||
* @throws IllegalStateException if this scope is not {@linkplain SegmentScope#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread {@code T},
|
||||
* such that {@code isAccessibleBy(T) == false}.
|
||||
*/
|
||||
void whileAlive(Runnable action);
|
||||
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 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
|
||||
@ -128,9 +128,22 @@ public sealed interface SequenceLayout extends MemoryLayout permits SequenceLayo
|
||||
*/
|
||||
SequenceLayout flatten();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
SequenceLayout withName(String name);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
MemoryLayout withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
* @throws IllegalArgumentException if {@code bitAlignment < elementLayout().bitAlignment()}.
|
||||
*/
|
||||
SequenceLayout withBitAlignment(long bitAlignment);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 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
|
||||
@ -39,9 +39,22 @@ import jdk.internal.javac.PreviewFeature;
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
||||
public sealed interface StructLayout extends GroupLayout permits StructLayoutImpl {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
StructLayout withName(String name);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
StructLayout withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
StructLayout withBitAlignment(long bitAlignment);
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -52,21 +52,23 @@ import java.util.function.BiFunction;
|
||||
* <ul>
|
||||
* <li>It can be passed to a {@link Linker} to create a downcall method handle, which can then be used to call the foreign function at the segment's address.</li>
|
||||
* <li>It can be passed to an existing {@linkplain Linker#downcallHandle(FunctionDescriptor, Linker.Option...) downcall method handle}, as an argument to the underlying foreign function.</li>
|
||||
* <li>It can be {@linkplain MemorySegment#set(ValueLayout.OfAddress, long, MemorySegment) stored} inside another memory segment.</li>
|
||||
* <li>It can be used to access the region of memory backing a global variable (this might require
|
||||
* {@link MemorySegment#ofAddress(long, long, SegmentScope) resizing} the segment first).</li>
|
||||
* <li>It can be {@linkplain MemorySegment#set(AddressLayout, long, MemorySegment) stored} inside another memory segment.</li>
|
||||
* <li>It can be used to access the region of memory backing a global variable (this requires
|
||||
* {@link MemorySegment#reinterpret(long)} () resizing} the segment first).</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2 id="obtaining">Obtaining a symbol lookup</h2>
|
||||
*
|
||||
* The factory methods {@link #libraryLookup(String, SegmentScope)} and {@link #libraryLookup(Path, SegmentScope)}
|
||||
* The factory methods {@link #libraryLookup(String, Arena)} and {@link #libraryLookup(Path, Arena)}
|
||||
* create a symbol lookup for a library known to the operating system. The library is specified by either its name or a path.
|
||||
* The library is loaded if not already loaded. The symbol lookup, which is known as a <em>library lookup</em>, is associated
|
||||
* with a {@linkplain SegmentScope scope}; when the scope becomes not {@link SegmentScope#isAlive()}, the library is unloaded:
|
||||
* The library is loaded if not already loaded. The symbol lookup, which is known as a <em>library lookup</em>, and its
|
||||
* lifetime is controlled by an {@linkplain Arena arena}. For instance, if the provided arena is a
|
||||
* confined arena, the library associated with the symbol lookup is unloaded when the confined arena
|
||||
* is {@linkplain Arena#close()}:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* try (Arena arena = Arena.openConfined()) {
|
||||
* SymbolLookup libGL = SymbolLookup.libraryLookup("libGL.so", arena.scope()); // libGL.so loaded here
|
||||
* try (Arena arena = Arena.ofConfined()) {
|
||||
* SymbolLookup libGL = SymbolLookup.libraryLookup("libGL.so", arena); // libGL.so loaded here
|
||||
* MemorySegment glGetString = libGL.find("glGetString").orElseThrow();
|
||||
* ...
|
||||
* } // libGL.so unloaded here
|
||||
@ -92,7 +94,7 @@ import java.util.function.BiFunction;
|
||||
* that were loaded in the course of creating a library lookup:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* libraryLookup("libGL.so", scope).find("glGetString").isPresent(); // true
|
||||
* libraryLookup("libGL.so", arena).find("glGetString").isPresent(); // true
|
||||
* loaderLookup().find("glGetString").isPresent(); // false
|
||||
*}
|
||||
*
|
||||
@ -101,7 +103,7 @@ import java.util.function.BiFunction;
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* System.loadLibrary("GL"); // libGL.so loaded here
|
||||
* libraryLookup("libGL.so", scope).find("glGetString").isPresent(); // true
|
||||
* libraryLookup("libGL.so", arena).find("glGetString").isPresent(); // true
|
||||
*}
|
||||
*
|
||||
* <p>
|
||||
@ -139,9 +141,10 @@ public interface SymbolLookup {
|
||||
* <p>
|
||||
* Libraries associated with a class loader are unloaded when the class loader becomes
|
||||
* <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>. The symbol lookup
|
||||
* returned by this method is backed by a scope that is always alive and which keeps the caller's
|
||||
* class loader reachable. Therefore, libraries associated with the caller's class
|
||||
* loader are kept loaded (and their symbols available) as long as a loader lookup for that class loader is reachable.
|
||||
* returned by this method is associated with a fresh {@linkplain MemorySegment.Scope scope} which keeps the caller's
|
||||
* class loader reachable. Therefore, libraries associated with the caller's class loader are kept loaded
|
||||
* (and their symbols available) as long as a loader lookup for that class loader, or any of the segments
|
||||
* obtained by it, is reachable.
|
||||
* <p>
|
||||
* In cases where this method is called from a context where there is no caller frame on the stack
|
||||
* (e.g. when called directly from a JNI attached thread), the caller's class loader defaults to the
|
||||
@ -158,9 +161,13 @@ public interface SymbolLookup {
|
||||
ClassLoader loader = caller != null ?
|
||||
caller.getClassLoader() :
|
||||
ClassLoader.getSystemClassLoader();
|
||||
SegmentScope loaderScope = (loader == null || loader instanceof BuiltinClassLoader) ?
|
||||
SegmentScope.global() : // builtin loaders never go away
|
||||
MemorySessionImpl.heapSession(loader);
|
||||
Arena loaderArena;// builtin loaders never go away
|
||||
if ((loader == null || loader instanceof BuiltinClassLoader)) {
|
||||
loaderArena = Arena.global();
|
||||
} else {
|
||||
MemorySessionImpl session = MemorySessionImpl.heapSession(loader);
|
||||
loaderArena = session.asArena();
|
||||
}
|
||||
return name -> {
|
||||
Objects.requireNonNull(name);
|
||||
JavaLangAccess javaLangAccess = SharedSecrets.getJavaLangAccess();
|
||||
@ -168,14 +175,17 @@ public interface SymbolLookup {
|
||||
long addr = javaLangAccess.findNative(loader, name);
|
||||
return addr == 0L ?
|
||||
Optional.empty() :
|
||||
Optional.of(MemorySegment.ofAddress(addr, 0L, loaderScope));
|
||||
Optional.of(MemorySegment.ofAddress(addr)
|
||||
.reinterpret(loaderArena, null));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a library with the given name (if not already loaded) and creates a symbol lookup for symbols in that library.
|
||||
* The library will be unloaded when the provided scope becomes
|
||||
* not {@linkplain SegmentScope#isAlive() alive}, if no other library lookup is still using it.
|
||||
* The lifetime of the returned library lookup is controlled by the provided arena.
|
||||
* For instance, if the provided arena is a confined arena, the library
|
||||
* associated with the returned lookup will be unloaded when the provided confined arena is
|
||||
* {@linkplain Arena#close() closed}.
|
||||
* @implNote The process of resolving a library name is OS-specific. For instance, in a POSIX-compliant OS,
|
||||
* the library name is resolved according to the specification of the {@code dlopen} function for that OS.
|
||||
* In Windows, the library name is resolved according to the specification of the {@code LoadLibrary} function.
|
||||
@ -186,21 +196,26 @@ public interface SymbolLookup {
|
||||
* restricted methods, and use safe and supported functionalities, where possible.
|
||||
*
|
||||
* @param name the name of the library in which symbols should be looked up.
|
||||
* @param scope the scope associated with symbols obtained from the returned lookup.
|
||||
* @param arena the arena associated with symbols obtained from the returned lookup.
|
||||
* @return a new symbol lookup suitable to find symbols in a library with the given name.
|
||||
* @throws IllegalStateException if {@code arena.scope().isAlive() == false}
|
||||
* @throws WrongThreadException if {@code arena} is a confined arena, and this method is called from a
|
||||
* thread {@code T}, other than the arena's owner thread.
|
||||
* @throws IllegalArgumentException if {@code name} does not identify a valid library.
|
||||
* @throws IllegalCallerException If the caller is in a module that does not have native access enabled.
|
||||
*/
|
||||
@CallerSensitive
|
||||
static SymbolLookup libraryLookup(String name, SegmentScope scope) {
|
||||
static SymbolLookup libraryLookup(String name, Arena arena) {
|
||||
Reflection.ensureNativeAccess(Reflection.getCallerClass(), SymbolLookup.class, "libraryLookup");
|
||||
return libraryLookup(name, RawNativeLibraries::load, scope);
|
||||
return libraryLookup(name, RawNativeLibraries::load, arena);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a library from the given path (if not already loaded) and creates a symbol lookup for symbols
|
||||
* in that library. The library will be unloaded when the provided scope becomes
|
||||
* not {@linkplain SegmentScope#isAlive() alive}, if no other library lookup is still using it.
|
||||
* in that library. The lifetime of the returned library lookup is controlled by the provided arena.
|
||||
* For instance, if the provided arena is a confined arena, the library
|
||||
* associated with the returned lookup will be unloaded when the provided confined arena is
|
||||
* {@linkplain Arena#close() closed}.
|
||||
* <p>
|
||||
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
|
||||
* Restricted methods are unsafe, and, if used incorrectly, their use might crash
|
||||
@ -210,20 +225,23 @@ public interface SymbolLookup {
|
||||
* @implNote On Linux, the functionalities provided by this factory method and the returned symbol lookup are
|
||||
* implemented using the {@code dlopen}, {@code dlsym} and {@code dlclose} functions.
|
||||
* @param path the path of the library in which symbols should be looked up.
|
||||
* @param scope the scope associated with symbols obtained from the returned lookup.
|
||||
* @param arena the arena associated with symbols obtained from the returned lookup.
|
||||
* @return a new symbol lookup suitable to find symbols in a library with the given path.
|
||||
* @throws IllegalStateException if {@code arena.scope().isAlive() == false}
|
||||
* @throws WrongThreadException if {@code arena} is a confined arena, and this method is called from a
|
||||
* thread {@code T}, other than the arena's owner thread.
|
||||
* @throws IllegalArgumentException if {@code path} does not point to a valid library.
|
||||
* @throws IllegalCallerException If the caller is in a module that does not have native access enabled.
|
||||
*/
|
||||
@CallerSensitive
|
||||
static SymbolLookup libraryLookup(Path path, SegmentScope scope) {
|
||||
static SymbolLookup libraryLookup(Path path, Arena arena) {
|
||||
Reflection.ensureNativeAccess(Reflection.getCallerClass(), SymbolLookup.class, "libraryLookup");
|
||||
return libraryLookup(path, RawNativeLibraries::load, scope);
|
||||
return libraryLookup(path, RawNativeLibraries::load, arena);
|
||||
}
|
||||
|
||||
private static <Z> SymbolLookup libraryLookup(Z libDesc, BiFunction<RawNativeLibraries, Z, NativeLibrary> loadLibraryFunc, SegmentScope libScope) {
|
||||
private static <Z> SymbolLookup libraryLookup(Z libDesc, BiFunction<RawNativeLibraries, Z, NativeLibrary> loadLibraryFunc, Arena libArena) {
|
||||
Objects.requireNonNull(libDesc);
|
||||
Objects.requireNonNull(libScope);
|
||||
Objects.requireNonNull(libArena);
|
||||
// attempt to load native library from path or name
|
||||
RawNativeLibraries nativeLibraries = RawNativeLibraries.newInstance(MethodHandles.lookup());
|
||||
NativeLibrary library = loadLibraryFunc.apply(nativeLibraries, libDesc);
|
||||
@ -231,7 +249,7 @@ public interface SymbolLookup {
|
||||
throw new IllegalArgumentException("Cannot open library: " + libDesc);
|
||||
}
|
||||
// register hook to unload library when 'libScope' becomes not alive
|
||||
((MemorySessionImpl) libScope).addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() {
|
||||
MemorySessionImpl.toMemorySession(libArena).addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() {
|
||||
@Override
|
||||
public void cleanup() {
|
||||
nativeLibraries.unload(library);
|
||||
@ -242,7 +260,8 @@ public interface SymbolLookup {
|
||||
long addr = library.find(name);
|
||||
return addr == 0L ?
|
||||
Optional.empty() :
|
||||
Optional.of(MemorySegment.ofAddress(addr, 0, libScope));
|
||||
Optional.of(MemorySegment.ofAddress(addr)
|
||||
.reinterpret(libArena, null));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 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
|
||||
@ -49,5 +49,12 @@ public sealed interface UnionLayout extends GroupLayout permits UnionLayoutImpl
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
UnionLayout withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
UnionLayout withBitAlignment(long bitAlignment);
|
||||
}
|
||||
|
||||
@ -1,352 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 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 java.lang.foreign;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64VaList;
|
||||
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64VaList;
|
||||
import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64VaList;
|
||||
import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64VaList;
|
||||
import jdk.internal.foreign.abi.x64.sysv.SysVVaList;
|
||||
import jdk.internal.foreign.abi.x64.windows.WinVaList;
|
||||
import jdk.internal.javac.PreviewFeature;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
|
||||
/**
|
||||
* Helper class to create and manipulate variable argument lists, similar in functionality to a C {@code va_list}.
|
||||
* <p>
|
||||
* A variable argument list can be created using the {@link #make(Consumer, SegmentScope)} factory, as follows:
|
||||
* {@snippet lang = java:
|
||||
* VaList vaList = VaList.make(builder ->
|
||||
* builder.addVarg(C_INT, 42)
|
||||
* .addVarg(C_DOUBLE, 3.8d));
|
||||
*}
|
||||
* Once created, clients can obtain the platform-dependent {@linkplain #segment() memory segment} associated with a variable
|
||||
* argument list, which can then be passed to {@linkplain Linker#downcallHandle(FunctionDescriptor, Linker.Option...) downcall method handles}
|
||||
* targeting native functions using the C {@code va_list} type.
|
||||
* <p>
|
||||
* The contents of a foreign memory segment modelling a variable argument list can be accessed by <em>unsafely</em> creating
|
||||
* a variable argument list, as follows:
|
||||
* {@snippet lang = java:
|
||||
* void upcall(int n, MemorySegment vaListSegment) {
|
||||
* try (Arena arena = Arena.openConfined()) {
|
||||
* VaList vaList = VaList.ofAddress(vaListSegment.address(), arena.scope());
|
||||
* VaList copy = vaList.copy();
|
||||
* int i = vaList.nextVarg(C_INT);
|
||||
* double d = vaList.nextVarg(C_DOUBLE);
|
||||
* // and again
|
||||
* int i = copy.nextVarg(C_INT);
|
||||
* double d = copy.nextVarg(C_DOUBLE);
|
||||
* }
|
||||
* }
|
||||
*}
|
||||
* The above method receives a foreign segment modelling a variable argument list; the contents of the segment are accessed by creating
|
||||
* a new variable argument list, from the segment address. Note that the variable argument list is first copied into
|
||||
* a second list before any element is accessed: this will allow us to iterate through the elements twice. Elements in
|
||||
* the variable argument list are accessed using {@link #nextVarg(ValueLayout.OfInt)} and
|
||||
* {@link #nextVarg(ValueLayout.OfDouble)}. These methods (as well as other access methods in the {@link VaList} class)
|
||||
* take the layout of the element that needs to be accessed and perform all the necessary alignment checks as well
|
||||
* as endianness conversions.
|
||||
* <p>
|
||||
* Per the C specification (see C99 standard 6.5.2.2 Function calls - item 6),
|
||||
* arguments to variadic calls are erased by way of 'default argument promotions',
|
||||
* which erases integral types by way of integer promotion (see C99 standard 6.3.1.1 - item 2),
|
||||
* and which erases all {@code float} arguments to {@code double}.
|
||||
* <p>
|
||||
* As such, this interface only supports reading {@code int}, {@code double},
|
||||
* and any other type that fits into a {@code long}.
|
||||
* <h2 id="safety">Safety considerations</h2>
|
||||
* Accessing a value through a variable argument list using the wrong memory layout will result in undefined behavior.
|
||||
* For instance, if a variable argument list currently points at a C {@code int} value, then accessing it using
|
||||
* {@link #nextVarg(ValueLayout.OfLong)} is illegal. Similarly, accessing the variable argument list with
|
||||
* {@link #skip(MemoryLayout...)}, and providing a layout other than {@link ValueLayout.OfInt} is illegal.
|
||||
* Any such illegal accesses might not be detected by the implementation, and can corrupt the variable argument list,
|
||||
* so that the behavior of subsequent accesses is also undefined.
|
||||
* <p>
|
||||
* It is possible for clients to access elements outside the spatial bounds of a variable argument list.
|
||||
* Variable argument list implementations will try to detect out-of-bounds reads on a best-effort basis.
|
||||
* <p>
|
||||
* Whether this detection succeeds depends on the factory method used to create the variable argument list:
|
||||
* <ul>
|
||||
* <li>Variable argument lists created <em>safely</em>, using {@link #make(Consumer, SegmentScope)} are capable of detecting out-of-bounds reads;</li>
|
||||
* <li>Variable argument lists created <em>unsafely</em>, using {@link #ofAddress(long, SegmentScope)} are not capable of detecting out-of-bounds reads</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* This class is not thread safe, and all accesses should occur within a single thread
|
||||
* (regardless of the scope used to obtain the variable arity list).
|
||||
*
|
||||
* @since 19
|
||||
*/
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
||||
public sealed interface VaList permits WinVaList, SysVVaList, LinuxAArch64VaList, MacOsAArch64VaList, WindowsAArch64VaList, LinuxRISCV64VaList, SharedUtils.EmptyVaList {
|
||||
|
||||
/**
|
||||
* Reads the next value as an {@code int} and advances this variable argument list's position. The behavior of this
|
||||
* method is equivalent to the C {@code va_arg} function.
|
||||
*
|
||||
* @param layout the layout of the value to be read.
|
||||
* @return the {@code int} value read from this variable argument list.
|
||||
* @throws IllegalStateException if the scope associated with this variable argument list is not
|
||||
* {@linkplain SegmentScope#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread {@code T},
|
||||
* such that {@code segment().scope().isAccessibleBy(T) == false}.
|
||||
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
int nextVarg(ValueLayout.OfInt layout);
|
||||
|
||||
/**
|
||||
* Reads the next value as a {@code long} and advances this variable argument list's position. The behavior of this
|
||||
* method is equivalent to the C {@code va_arg} function.
|
||||
*
|
||||
* @param layout the layout of the value to be read.
|
||||
* @return the {@code long} value read from this variable argument list.
|
||||
* @throws IllegalStateException if the scope associated with this variable argument list is not
|
||||
* {@linkplain SegmentScope#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread {@code T},
|
||||
* such that {@code segment().scope().isAccessibleBy(T) == false}.
|
||||
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
long nextVarg(ValueLayout.OfLong layout);
|
||||
|
||||
/**
|
||||
* Reads the next value as a {@code double} and advances this variable argument list's position. The behavior of this
|
||||
* method is equivalent to the C {@code va_arg} function.
|
||||
*
|
||||
* @param layout the layout of the value
|
||||
* @return the {@code double} value read from this variable argument list.
|
||||
* @throws IllegalStateException if the scope associated with this variable argument list is not
|
||||
* {@linkplain SegmentScope#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread {@code T},
|
||||
* such that {@code segment().scope().isAccessibleBy(T) == false}.
|
||||
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
double nextVarg(ValueLayout.OfDouble layout);
|
||||
|
||||
/**
|
||||
* Reads the next address value, wraps it into a native segment, and advances this variable argument list's position.
|
||||
* The behavior of this method is equivalent to the C {@code va_arg} function. The returned segment's base
|
||||
* {@linkplain MemorySegment#address()} is set to the value read from the variable argument list, and the segment
|
||||
* is associated with the {@linkplain SegmentScope#global() global scope}. Under normal conditions, the size of the returned
|
||||
* segment is {@code 0}. However, if the provided layout is an {@linkplain ValueLayout.OfAddress#asUnbounded() unbounded}
|
||||
* address layout, then the size of the returned segment is {@code Long.MAX_VALUE}.
|
||||
*
|
||||
* @param layout the layout of the value to be read.
|
||||
* @return a native segment whose {@linkplain MemorySegment#address() address} is the value read from
|
||||
* this variable argument list.
|
||||
* @throws IllegalStateException if the scope associated with this variable argument list is not
|
||||
* {@linkplain SegmentScope#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread {@code T},
|
||||
* such that {@code segment().scope().isAccessibleBy(T) == false}.
|
||||
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
MemorySegment nextVarg(ValueLayout.OfAddress layout);
|
||||
|
||||
/**
|
||||
* Reads the next composite value into a new {@code MemorySegment}, allocated with the provided allocator,
|
||||
* and advances this variable argument list's position. The behavior of this method is equivalent to the C
|
||||
* {@code va_arg} function. The provided group layout must correspond to a C struct or union type.
|
||||
* <p>
|
||||
* How the value is read in the returned segment is ABI-dependent: calling this method on a group layout
|
||||
* with member layouts {@code L_1, L_2, ... L_n} is not guaranteed to be semantically equivalent to perform distinct
|
||||
* calls to {@code nextVarg} for each of the layouts in {@code L_1, L_2, ... L_n}.
|
||||
* <p>
|
||||
* The memory segment returned by this method will be allocated using the given {@link SegmentAllocator}.
|
||||
*
|
||||
* @param layout the layout of the value to be read.
|
||||
* @param allocator the allocator to be used to create a segment where the contents of the variable argument list
|
||||
* will be copied.
|
||||
* @return the {@code MemorySegment} value read from this variable argument list.
|
||||
* @throws IllegalStateException if the scope associated with this variable argument list is not
|
||||
* {@linkplain SegmentScope#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread {@code T},
|
||||
* such that {@code segment().scope().isAccessibleBy(T) == false}.
|
||||
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator);
|
||||
|
||||
/**
|
||||
* Skips a number of elements with the given memory layouts, and advances this variable argument list's position.
|
||||
*
|
||||
* @param layouts the layouts of the values to be skipped.
|
||||
* @throws IllegalStateException if the scope associated with this variable argument list is not
|
||||
* {@linkplain SegmentScope#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread {@code T},
|
||||
* such that {@code segment().scope().isAccessibleBy(T) == false}.
|
||||
* @throws NoSuchElementException if an <a href=VaList.html#safety>out-of-bounds</a> read is detected.
|
||||
*/
|
||||
void skip(MemoryLayout... layouts);
|
||||
|
||||
/**
|
||||
* Copies this variable argument list at its current position into a new variable argument list associated
|
||||
* with the same scope as this variable argument list. The behavior of this method is equivalent to the C
|
||||
* {@code va_copy} function.
|
||||
* <p>
|
||||
* Copying is useful to traverse the variable argument list elements, starting from the current position,
|
||||
* without affecting the state of the original variable argument list, essentially allowing the elements to be
|
||||
* traversed multiple times.
|
||||
*
|
||||
* @return a copy of this variable argument list.
|
||||
* @throws IllegalStateException if the scope associated with this variable argument list is not
|
||||
* {@linkplain SegmentScope#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread {@code T},
|
||||
* such that {@code segment().scope().isAccessibleBy(T) == false}.
|
||||
*/
|
||||
VaList copy();
|
||||
|
||||
/**
|
||||
* Returns a zero-length {@linkplain MemorySegment memory segment} associated with this variable argument list.
|
||||
* The contents of the returned memory segment are platform-dependent. Whether and how the contents of
|
||||
* the returned segment are updated when iterating the contents of a variable argument list is also
|
||||
* platform-dependent.
|
||||
* @return a zero-length {@linkplain MemorySegment memory segment} associated with this variable argument list.
|
||||
*/
|
||||
MemorySegment segment();
|
||||
|
||||
/**
|
||||
* Creates a variable argument list from the give address value and scope. The address is typically obtained
|
||||
* by calling {@link MemorySegment#address()} on a foreign memory segment instance. The provided scope determines
|
||||
* the lifecycle of the returned variable argument list: the returned variable argument list will no longer be accessible,
|
||||
* and its associated off-heap memory region will be deallocated when the scope becomes not
|
||||
* {@linkplain SegmentScope#isAlive() alive}.
|
||||
* <p>
|
||||
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
|
||||
* Restricted methods are unsafe, and, if used incorrectly, their use might crash
|
||||
* the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
|
||||
* restricted methods, and use safe and supported functionalities, where possible.
|
||||
*
|
||||
* @param address the address of the variable argument list.
|
||||
* @param scope the scope associated with the returned variable argument list.
|
||||
* @return a new variable argument list backed by an off-heap region of memory starting at the given address value.
|
||||
* @throws IllegalStateException if {@code scope} is not {@linkplain SegmentScope#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread {@code T},
|
||||
* such that {@code scope.isAccessibleBy(T) == false}.
|
||||
* @throws UnsupportedOperationException if the underlying native platform is not supported.
|
||||
* @throws IllegalCallerException If the caller is in a module that does not have native access enabled.
|
||||
*/
|
||||
@CallerSensitive
|
||||
static VaList ofAddress(long address, SegmentScope scope) {
|
||||
Reflection.ensureNativeAccess(Reflection.getCallerClass(), VaList.class, "ofAddress");
|
||||
Objects.requireNonNull(scope);
|
||||
return SharedUtils.newVaListOfAddress(address, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a variable argument list using a builder (see {@link Builder}), with the given
|
||||
* scope. The provided scope determines the lifecycle of the returned variable argument list: the
|
||||
* returned variable argument list will no longer be accessible, and its associated off-heap memory region will be
|
||||
* deallocated when the scope becomes not {@linkplain SegmentScope#isAlive() alive}.
|
||||
* <p>
|
||||
* Note that when there are no elements added to the created va list,
|
||||
* this method will return the same as {@link #empty()}.
|
||||
*
|
||||
* @implNote variable argument lists created using this method can detect <a href=VaList.html#safety>out-of-bounds</a> reads.
|
||||
*
|
||||
* @param actions a consumer for a builder (see {@link Builder}) which can be used to specify the elements
|
||||
* of the underlying variable argument list.
|
||||
* @param scope the scope to be associated with the new variable arity list.
|
||||
* @return a new variable argument list.
|
||||
* @throws UnsupportedOperationException if the underlying native platform is not supported.
|
||||
* @throws IllegalStateException if {@code scope} is not {@linkplain SegmentScope#isAlive() alive}.
|
||||
* @throws WrongThreadException if this method is called from a thread {@code T},
|
||||
* such that {@code scope.isAccessibleBy(T) == false}.
|
||||
*/
|
||||
static VaList make(Consumer<Builder> actions, SegmentScope scope) {
|
||||
Objects.requireNonNull(actions);
|
||||
Objects.requireNonNull(scope);
|
||||
return SharedUtils.newVaList(actions, scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an empty variable argument list, associated with the {@linkplain SegmentScope#global() global scope}.
|
||||
* The resulting variable argument list does not contain any argument, and throws {@link UnsupportedOperationException}
|
||||
* on all operations, except for {@link VaList#segment()}, {@link VaList#copy()}.
|
||||
* @return an empty variable argument list.
|
||||
* @throws UnsupportedOperationException if the underlying native platform is not supported.
|
||||
*/
|
||||
static VaList empty() {
|
||||
return SharedUtils.emptyVaList();
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder used to construct a {@linkplain VaList variable argument list}.
|
||||
*
|
||||
* @since 19
|
||||
*/
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
||||
sealed interface Builder permits WinVaList.Builder, SysVVaList.Builder, LinuxAArch64VaList.Builder, MacOsAArch64VaList.Builder, WindowsAArch64VaList.Builder, LinuxRISCV64VaList.Builder {
|
||||
|
||||
/**
|
||||
* Writes an {@code int} value to the variable argument list being constructed.
|
||||
*
|
||||
* @param layout the layout of the value to be written.
|
||||
* @param value the {@code int} value to be written.
|
||||
* @return this builder.
|
||||
*/
|
||||
Builder addVarg(ValueLayout.OfInt layout, int value);
|
||||
|
||||
/**
|
||||
* Writes a {@code long} value to the variable argument list being constructed.
|
||||
*
|
||||
* @param layout the layout of the value to be written.
|
||||
* @param value the {@code long} value to be written.
|
||||
* @return this builder.
|
||||
*/
|
||||
Builder addVarg(ValueLayout.OfLong layout, long value);
|
||||
|
||||
/**
|
||||
* Writes a {@code double} value to the variable argument list being constructed.
|
||||
*
|
||||
* @param layout the layout of the value to be written.
|
||||
* @param value the {@code double} value to be written.
|
||||
* @return this builder.
|
||||
*/
|
||||
Builder addVarg(ValueLayout.OfDouble layout, double value);
|
||||
|
||||
/**
|
||||
* Writes the {@linkplain MemorySegment#address() address} of the provided native segment
|
||||
* to the variable argument list being constructed.
|
||||
*
|
||||
* @param layout the layout of the value to be written.
|
||||
* @param segment the segment whose {@linkplain MemorySegment#address() address} is to be written.
|
||||
* @return this builder.
|
||||
*/
|
||||
Builder addVarg(ValueLayout.OfAddress layout, MemorySegment segment);
|
||||
|
||||
/**
|
||||
* Writes a {@code MemorySegment}, with the given layout, to the variable argument list being constructed.
|
||||
*
|
||||
* @param layout the layout of the value to be written.
|
||||
* @param value the {@code MemorySegment} whose contents will be copied.
|
||||
* @return this builder.
|
||||
*/
|
||||
Builder addVarg(GroupLayout layout, MemorySegment value);
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 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
|
||||
@ -31,7 +31,6 @@ import java.nio.ByteOrder;
|
||||
|
||||
import jdk.internal.foreign.layout.ValueLayouts;
|
||||
import jdk.internal.javac.PreviewFeature;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
|
||||
/**
|
||||
* A layout that models values of basic data types. Examples of values modelled by a value layout are
|
||||
@ -49,10 +48,13 @@ import jdk.internal.reflect.CallerSensitive;
|
||||
*
|
||||
* @implSpec implementing classes and subclasses are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
|
||||
*
|
||||
* @sealedGraph
|
||||
* @since 19
|
||||
*/
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
||||
public sealed interface ValueLayout extends MemoryLayout {
|
||||
public sealed interface ValueLayout extends MemoryLayout permits
|
||||
ValueLayout.OfBoolean, ValueLayout.OfByte, ValueLayout.OfChar, ValueLayout.OfShort, ValueLayout.OfInt,
|
||||
ValueLayout.OfFloat, ValueLayout.OfLong, ValueLayout.OfDouble, AddressLayout {
|
||||
|
||||
/**
|
||||
* {@return the value's byte order}
|
||||
@ -68,6 +70,12 @@ public sealed interface ValueLayout extends MemoryLayout {
|
||||
*/
|
||||
ValueLayout withOrder(ByteOrder order);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
ValueLayout withoutName();
|
||||
|
||||
/**
|
||||
* Creates a <em>strided</em> var handle that can be used to access a memory segment as multi-dimensional
|
||||
* array. The layout of this array is a sequence layout with {@code shape.length} nested sequence layouts. The element
|
||||
@ -141,9 +149,9 @@ public sealed interface ValueLayout extends MemoryLayout {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
|
||||
ValueLayout withBitAlignment(long bitAlignment);
|
||||
|
||||
/**
|
||||
@ -165,6 +173,13 @@ public sealed interface ValueLayout extends MemoryLayout {
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfBoolean withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfBoolean withBitAlignment(long bitAlignment);
|
||||
|
||||
/**
|
||||
@ -194,6 +209,13 @@ public sealed interface ValueLayout extends MemoryLayout {
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfByte withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfByte withBitAlignment(long bitAlignment);
|
||||
|
||||
/**
|
||||
@ -224,6 +246,13 @@ public sealed interface ValueLayout extends MemoryLayout {
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfChar withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfChar withBitAlignment(long bitAlignment);
|
||||
|
||||
/**
|
||||
@ -254,6 +283,13 @@ public sealed interface ValueLayout extends MemoryLayout {
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfShort withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfShort withBitAlignment(long bitAlignment);
|
||||
|
||||
/**
|
||||
@ -284,6 +320,13 @@ public sealed interface ValueLayout extends MemoryLayout {
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfInt withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfInt withBitAlignment(long bitAlignment);
|
||||
|
||||
/**
|
||||
@ -310,6 +353,12 @@ public sealed interface ValueLayout extends MemoryLayout {
|
||||
@Override
|
||||
OfFloat withName(String name);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfFloat withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@ -344,6 +393,13 @@ public sealed interface ValueLayout extends MemoryLayout {
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfLong withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfLong withBitAlignment(long bitAlignment);
|
||||
|
||||
/**
|
||||
@ -374,6 +430,13 @@ public sealed interface ValueLayout extends MemoryLayout {
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfDouble withoutName();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @throws IllegalArgumentException {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfDouble withBitAlignment(long bitAlignment);
|
||||
|
||||
/**
|
||||
@ -384,146 +447,57 @@ public sealed interface ValueLayout extends MemoryLayout {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A value layout whose carrier is {@code MemorySegment.class}.
|
||||
*
|
||||
* @see #ADDRESS
|
||||
* @see #ADDRESS_UNALIGNED
|
||||
* @since 19
|
||||
*/
|
||||
@PreviewFeature(feature = PreviewFeature.Feature.FOREIGN)
|
||||
sealed interface OfAddress extends ValueLayout permits ValueLayouts.OfAddressImpl {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfAddress withName(String name);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfAddress withBitAlignment(long bitAlignment);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
OfAddress withOrder(ByteOrder order);
|
||||
|
||||
/**
|
||||
* Returns an <em>unbounded</em> address layout with the same carrier, alignment constraint, name and order as this address layout,
|
||||
* but with the specified pointee layout. An unbounded address layout allow raw addresses to be accessed
|
||||
* as {@linkplain MemorySegment memory segments} whose size is set to {@link Long#MAX_VALUE}. As such,
|
||||
* these segments can be used in subsequent access operations.
|
||||
* <p>
|
||||
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
|
||||
* Restricted methods are unsafe, and, if used incorrectly, their use might crash
|
||||
* the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
|
||||
* restricted methods, and use safe and supported functionalities, where possible.
|
||||
*
|
||||
* @return an unbounded address layout with same characteristics as this layout.
|
||||
* @throws IllegalCallerException If the caller is in a module that does not have native access enabled.
|
||||
* @see #isUnbounded()
|
||||
*/
|
||||
@CallerSensitive
|
||||
OfAddress asUnbounded();
|
||||
|
||||
/**
|
||||
* {@return {@code true}, if this address layout is an {@linkplain #asUnbounded() unbounded address layout}}.
|
||||
*/
|
||||
boolean isUnbounded();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A value layout constant whose size is the same as that of a machine address ({@code size_t}),
|
||||
* bit alignment set to {@code sizeof(size_t) * 8}, byte order set to {@link ByteOrder#nativeOrder()}.
|
||||
* Equivalent to the following code:
|
||||
* {@snippet lang=java :
|
||||
* MemoryLayout.valueLayout(MemorySegment.class, ByteOrder.nativeOrder());
|
||||
* }
|
||||
*/
|
||||
OfAddress ADDRESS = ValueLayouts.OfAddressImpl.of(ByteOrder.nativeOrder());
|
||||
AddressLayout ADDRESS = ValueLayouts.OfAddressImpl.of(ByteOrder.nativeOrder());
|
||||
|
||||
/**
|
||||
* A value layout constant whose size is the same as that of a Java {@code byte},
|
||||
* bit alignment set to 8, and byte order set to {@link ByteOrder#nativeOrder()}.
|
||||
* Equivalent to the following code:
|
||||
* {@snippet lang=java :
|
||||
* MemoryLayout.valueLayout(byte.class, ByteOrder.nativeOrder());
|
||||
* }
|
||||
*/
|
||||
OfByte JAVA_BYTE = ValueLayouts.OfByteImpl.of(ByteOrder.nativeOrder());
|
||||
|
||||
/**
|
||||
* A value layout constant whose size is the same as that of a Java {@code boolean},
|
||||
* bit alignment set to 8, and byte order set to {@link ByteOrder#nativeOrder()}.
|
||||
* Equivalent to the following code:
|
||||
* {@snippet lang=java :
|
||||
* MemoryLayout.valueLayout(boolean.class, ByteOrder.nativeOrder());
|
||||
* }
|
||||
*/
|
||||
OfBoolean JAVA_BOOLEAN = ValueLayouts.OfBooleanImpl.of(ByteOrder.nativeOrder());
|
||||
|
||||
/**
|
||||
* A value layout constant whose size is the same as that of a Java {@code char},
|
||||
* bit alignment set to 16, and byte order set to {@link ByteOrder#nativeOrder()}.
|
||||
* Equivalent to the following code:
|
||||
* {@snippet lang=java :
|
||||
* MemoryLayout.valueLayout(char.class, ByteOrder.nativeOrder());
|
||||
* }
|
||||
*/
|
||||
OfChar JAVA_CHAR = ValueLayouts.OfCharImpl.of(ByteOrder.nativeOrder());
|
||||
|
||||
/**
|
||||
* A value layout constant whose size is the same as that of a Java {@code short},
|
||||
* bit alignment set to 16, and byte order set to {@link ByteOrder#nativeOrder()}.
|
||||
* Equivalent to the following code:
|
||||
* {@snippet lang=java :
|
||||
* MemoryLayout.valueLayout(short.class, ByteOrder.nativeOrder());
|
||||
* }
|
||||
*/
|
||||
OfShort JAVA_SHORT = ValueLayouts.OfShortImpl.of(ByteOrder.nativeOrder());
|
||||
|
||||
/**
|
||||
* A value layout constant whose size is the same as that of a Java {@code int},
|
||||
* bit alignment set to 32, and byte order set to {@link ByteOrder#nativeOrder()}.
|
||||
* Equivalent to the following code:
|
||||
* {@snippet lang=java :
|
||||
* MemoryLayout.valueLayout(int.class, ByteOrder.nativeOrder());
|
||||
* }
|
||||
*/
|
||||
OfInt JAVA_INT = ValueLayouts.OfIntImpl.of(ByteOrder.nativeOrder());
|
||||
|
||||
/**
|
||||
* A value layout constant whose size is the same as that of a Java {@code long},
|
||||
* bit alignment set to 64, and byte order set to {@link ByteOrder#nativeOrder()}.
|
||||
* Equivalent to the following code:
|
||||
* {@snippet lang=java :
|
||||
* MemoryLayout.valueLayout(long.class, ByteOrder.nativeOrder());
|
||||
* }
|
||||
*/
|
||||
OfLong JAVA_LONG = ValueLayouts.OfLongImpl.of(ByteOrder.nativeOrder());
|
||||
|
||||
/**
|
||||
* A value layout constant whose size is the same as that of a Java {@code float},
|
||||
* bit alignment set to 32, and byte order set to {@link ByteOrder#nativeOrder()}.
|
||||
* Equivalent to the following code:
|
||||
* {@snippet lang=java :
|
||||
* MemoryLayout.valueLayout(float.class, ByteOrder.nativeOrder()).withBitAlignment(32);
|
||||
* }
|
||||
*/
|
||||
OfFloat JAVA_FLOAT = ValueLayouts.OfFloatImpl.of(ByteOrder.nativeOrder());
|
||||
|
||||
/**
|
||||
* A value layout constant whose size is the same as that of a Java {@code double},
|
||||
* bit alignment set to 64, and byte order set to {@link ByteOrder#nativeOrder()}.
|
||||
* Equivalent to the following code:
|
||||
* {@snippet lang=java :
|
||||
* MemoryLayout.valueLayout(double.class, ByteOrder.nativeOrder());
|
||||
* }
|
||||
*/
|
||||
OfDouble JAVA_DOUBLE = ValueLayouts.OfDoubleImpl.of(ByteOrder.nativeOrder());
|
||||
|
||||
@ -537,7 +511,7 @@ public sealed interface ValueLayout extends MemoryLayout {
|
||||
* @apiNote Care should be taken when using unaligned value layouts as they may induce
|
||||
* performance and portability issues.
|
||||
*/
|
||||
OfAddress ADDRESS_UNALIGNED = ADDRESS.withBitAlignment(8);
|
||||
AddressLayout ADDRESS_UNALIGNED = ADDRESS.withBitAlignment(8);
|
||||
|
||||
/**
|
||||
* An unaligned value layout constant whose size is the same as that of a Java {@code char}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 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
|
||||
@ -31,77 +31,48 @@
|
||||
*
|
||||
* <p>
|
||||
* The main abstraction introduced to support foreign memory access is {@link java.lang.foreign.MemorySegment}, which
|
||||
* models a contiguous region of memory, residing either inside or outside the Java heap. The contents of a memory
|
||||
* segment can be described using a {@link java.lang.foreign.MemoryLayout memory layout}, which provides
|
||||
* basic operations to query sizes, offsets and alignment constraints. Memory layouts also provide
|
||||
* an alternate, more abstract way, to <a href=MemorySegment.html#segment-deref>access memory segments</a>
|
||||
* using {@linkplain java.lang.foreign.MemoryLayout#varHandle(java.lang.foreign.MemoryLayout.PathElement...) var handles},
|
||||
* models a contiguous region of memory, residing either inside or outside the Java heap. Memory segments are
|
||||
* typically allocated using an {@link java.lang.foreign.Arena}, which controls the lifetime of the regions of memory
|
||||
* backing the segments it allocates. The contents of a memory segment can be described using a
|
||||
* {@link java.lang.foreign.MemoryLayout memory layout}, which provides basic operations to query sizes, offsets and
|
||||
* alignment constraints. Memory layouts also provide an alternate, more abstract way, to
|
||||
* <a href=MemorySegment.html#segment-deref>access memory segments</a> using
|
||||
* {@linkplain java.lang.foreign.MemoryLayout#varHandle(java.lang.foreign.MemoryLayout.PathElement...) var handles},
|
||||
* which can be computed using <a href="MemoryLayout.html#layout-paths"><em>layout paths</em></a>.
|
||||
*
|
||||
* For example, to allocate an off-heap region of memory big enough to hold 10 values of the primitive type {@code int},
|
||||
* and fill it with values ranging from {@code 0} to {@code 9}, we can use the following code:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* MemorySegment segment = MemorySegment.allocateNative(10 * 4, SegmentScope.auto());
|
||||
* for (int i = 0 ; i < 10 ; i++) {
|
||||
* segment.setAtIndex(ValueLayout.JAVA_INT, i, i);
|
||||
* }
|
||||
*}
|
||||
*
|
||||
* This code creates a <em>native</em> memory segment, that is, a memory segment backed by
|
||||
* off-heap memory; the size of the segment is 40 bytes, enough to store 10 values of the primitive type {@code int}.
|
||||
* The segment is associated with an {@linkplain java.lang.foreign.SegmentScope#auto() automatic scope}. This
|
||||
* means that the off-heap region of memory backing the segment is managed, automatically, by the garbage collector.
|
||||
* As such, the off-heap memory backing the native segment will be released at some unspecified
|
||||
* point <em>after</em> the segment becomes <a href="../../../java/lang/ref/package.html#reachability">unreachable</a>.
|
||||
* This is similar to what happens with direct buffers created via {@link java.nio.ByteBuffer#allocateDirect(int)}.
|
||||
* It is also possible to manage the lifecycle of allocated native segments more directly, as shown in a later section.
|
||||
* <p>
|
||||
* Inside a loop, we then initialize the contents of the memory segment; note how the
|
||||
* {@linkplain java.lang.foreign.MemorySegment#setAtIndex(ValueLayout.OfInt, long, int) access method}
|
||||
* accepts a {@linkplain java.lang.foreign.ValueLayout value layout}, which specifies the size, alignment constraint,
|
||||
* byte order as well as the Java type ({@code int}, in this case) associated with the access operation. More specifically,
|
||||
* if we view the memory segment as a set of 10 adjacent slots, {@code s[i]}, where {@code 0 <= i < 10},
|
||||
* where the size of each slot is exactly 4 bytes, the initialization logic above will set each slot
|
||||
* so that {@code s[i] = i}, again where {@code 0 <= i < 10}.
|
||||
*
|
||||
* <h3 id="deallocation">Deterministic deallocation</h3>
|
||||
*
|
||||
* When writing code that manipulates memory segments, especially if backed by memory which resides outside the Java heap, it is
|
||||
* often crucial that the resources associated with a memory segment are released when the segment is no longer in use,
|
||||
* and in a timely fashion. For this reason, there might be cases where waiting for the garbage collector to determine that a segment
|
||||
* is <a href="../../../java/lang/ref/package.html#reachability">unreachable</a> is not optimal.
|
||||
* Clients that operate under these assumptions might want to programmatically release the memory backing a memory segment.
|
||||
* This can be done, using the {@link java.lang.foreign.Arena} abstraction, as shown below:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* try (Arena arena = Arena.openConfined()) {
|
||||
* try (Arena arena = Arena.ofConfined()) {
|
||||
* MemorySegment segment = arena.allocate(10 * 4);
|
||||
* for (int i = 0 ; i < 10 ; i++) {
|
||||
* segment.setAtIndex(ValueLayout.JAVA_INT, i, i);
|
||||
* }
|
||||
* }
|
||||
*}
|
||||
* }
|
||||
*
|
||||
* This example is almost identical to the prior one; this time we first create an arena
|
||||
* which is used to allocate multiple native segments which share the same life-cycle. That is, all the segments
|
||||
* allocated by the arena will be associated with the same {@linkplain java.lang.foreign.SegmentScope scope}.
|
||||
* Note the use of the <em>try-with-resources</em> construct: this idiom ensures that the off-heap region of memory backing the
|
||||
* native segment will be released at the end of the block, according to the semantics described in Section {@jls 14.20.3}
|
||||
* of <cite>The Java Language Specification</cite>.
|
||||
*
|
||||
* <h3 id="safety">Safety</h3>
|
||||
*
|
||||
* This API provides strong safety guarantees when it comes to memory access. First, when dereferencing a memory segment,
|
||||
* This code creates a <em>native</em> memory segment, that is, a memory segment backed by
|
||||
* off-heap memory; the size of the segment is 40 bytes, enough to store 10 values of the primitive type {@code int}.
|
||||
* The native segment is allocated using a {@linkplain java.lang.foreign.Arena#ofConfined() confined arena}.
|
||||
* As such, access to the native segment is restricted to the current thread (the thread that created the arena).
|
||||
* Moreover, when the arena is closed, the native segment is invalidated, and its backing region of memory is
|
||||
* deallocated. Note the use of the <em>try-with-resources</em> construct: this idiom ensures that the off-heap region
|
||||
* of memory backing the native segment will be released at the end of the block, according to the semantics described
|
||||
* in Section {@jls 14.20.3} of <cite>The Java Language Specification</cite>.
|
||||
* <p>
|
||||
* Memory segments provide strong safety guarantees when it comes to memory access. First, when accessing a memory segment,
|
||||
* the access coordinates are validated (upon access), to make sure that access does not occur at any address which resides
|
||||
* <em>outside</em> the boundaries of the memory segment used by the access operation. We call this guarantee <em>spatial safety</em>;
|
||||
* in other words, access to memory segments is bounds-checked, in the same way as array access is, as described in
|
||||
* Section {@jls 15.10.4} of <cite>The Java Language Specification</cite>.
|
||||
* <p>
|
||||
* Since memory segments created with an arena can become invalid (see above), segments are also validated (upon access) to make sure that
|
||||
* the scope associated with the segment being accessed is still alive.
|
||||
* We call this guarantee <em>temporal safety</em>. Together, spatial and temporal safety ensure that each memory access
|
||||
* operation either succeeds - and accesses a valid location within the region of memory backing the memory segment - or fails.
|
||||
* Additionally, to prevent a region of memory from being accessed <em>after</em> it has been deallocated
|
||||
* (i.e. <em>use-after-free</em>), a segment is also validated (upon access) to make sure that the arena from which it
|
||||
* has been obtained has not been closed. We call this guarantee <em>temporal safety</em>.
|
||||
* <p>
|
||||
* Together, spatial and temporal safety ensure that each memory access operation either succeeds - and accesses a valid
|
||||
* location within the region of memory backing the memory segment - or fails.
|
||||
*
|
||||
* <h2 id="ffa">Foreign function access</h2>
|
||||
* The key abstractions introduced to support foreign function access are {@link java.lang.foreign.SymbolLookup},
|
||||
@ -111,7 +82,7 @@
|
||||
* so that clients can perform foreign function calls directly in Java, without the need for intermediate layers of C/C++
|
||||
* code (as is the case with the <a href="{@docRoot}/../specs/jni/index.html">Java Native Interface (JNI)</a>).
|
||||
* <p>
|
||||
* For example, to compute the length of a string using the C standard library function {@code strlen} on a Linux x64 platform,
|
||||
* For example, to compute the length of a string using the C standard library function {@code strlen} on a Linux/x64 platform,
|
||||
* we can use the following code:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
@ -122,90 +93,39 @@
|
||||
* FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS)
|
||||
* );
|
||||
*
|
||||
* try (Arena arena = Arena.openConfined()) {
|
||||
* try (Arena arena = Arena.ofConfined()) {
|
||||
* MemorySegment cString = arena.allocateUtf8String("Hello");
|
||||
* long len = (long)strlen.invoke(cString); // 5
|
||||
* long len = (long)strlen.invokeExact(cString); // 5
|
||||
* }
|
||||
*}
|
||||
*
|
||||
* Here, we obtain a {@linkplain java.lang.foreign.Linker#nativeLinker() native linker} and we use it
|
||||
* to {@linkplain java.lang.foreign.SymbolLookup#find(java.lang.String) look up} the {@code strlen} symbol in the
|
||||
* standard C library; a <em>downcall method handle</em> targeting said symbol is subsequently
|
||||
* to {@linkplain java.lang.foreign.SymbolLookup#find(java.lang.String) look up} the {@code strlen} function in the
|
||||
* standard C library; a <em>downcall method handle</em> targeting said function is subsequently
|
||||
* {@linkplain java.lang.foreign.Linker#downcallHandle(FunctionDescriptor, Linker.Option...) obtained}.
|
||||
* To complete the linking successfully, we must provide a {@link java.lang.foreign.FunctionDescriptor} instance,
|
||||
* describing the signature of the {@code strlen} function.
|
||||
* From this information, the linker will uniquely determine the sequence of steps which will turn
|
||||
* the method handle invocation (here performed using {@link java.lang.invoke.MethodHandle#invoke(java.lang.Object...)})
|
||||
* the method handle invocation (here performed using {@link java.lang.invoke.MethodHandle#invokeExact(java.lang.Object...)})
|
||||
* into a foreign function call, according to the rules specified by the ABI of the underlying platform.
|
||||
* The {@link java.lang.foreign.Arena} class also provides many useful methods for
|
||||
* interacting with foreign code, such as
|
||||
* {@linkplain java.lang.foreign.SegmentAllocator#allocateUtf8String(java.lang.String) converting} Java strings into
|
||||
* zero-terminated, UTF-8 strings, as demonstrated in the above example.
|
||||
*
|
||||
* <h3 id="upcalls">Upcalls</h3>
|
||||
* The {@link java.lang.foreign.Linker} interface also allows clients to turn an existing method handle (which might point
|
||||
* to a Java method) into a memory segment, so that Java code can effectively be passed to other foreign functions.
|
||||
* For instance, we can write a method that compares two integer values, as follows:
|
||||
*
|
||||
* {@snippet lang=java :
|
||||
* class IntComparator {
|
||||
* static int intCompare(MemorySegment addr1, MemorySegment addr2) {
|
||||
* return addr1.get(ValueLayout.JAVA_INT, 0) -
|
||||
* addr2.get(ValueLayout.JAVA_INT, 0);
|
||||
*
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* The above method accesses two foreign memory segments containing an integer value, and performs a simple comparison
|
||||
* by returning the difference between such values. We can then obtain a method handle which targets the above static
|
||||
* method, as follows:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* FunctionDescriptor intCompareDescriptor = FunctionDescriptor.of(ValueLayout.JAVA_INT,
|
||||
* ValueLayout.ADDRESS.asUnbounded(),
|
||||
* ValueLayout.ADDRESS.asUnbounded());
|
||||
* MethodHandle intCompareHandle = MethodHandles.lookup().findStatic(IntComparator.class,
|
||||
* "intCompare",
|
||||
* intCompareDescriptor.toMethodType());
|
||||
*}
|
||||
*
|
||||
* As before, we need to create a {@link java.lang.foreign.FunctionDescriptor} instance, this time describing the signature
|
||||
* of the function pointer we want to create. The descriptor can be used to
|
||||
* {@linkplain java.lang.foreign.FunctionDescriptor#toMethodType() derive} a method type
|
||||
* that can be used to look up the method handle for {@code IntComparator.intCompare}.
|
||||
* <p>
|
||||
* Now that we have a method handle instance, we can turn it into a fresh function pointer,
|
||||
* using the {@link java.lang.foreign.Linker} interface, as follows:
|
||||
*
|
||||
* {@snippet lang = java:
|
||||
* SegmentScope scope = ...
|
||||
* MemorySegment comparFunc = Linker.nativeLinker().upcallStub(
|
||||
* intCompareHandle, intCompareDescriptor, scope);
|
||||
* );
|
||||
*}
|
||||
*
|
||||
* The {@link java.lang.foreign.FunctionDescriptor} instance created in the previous step is then used to
|
||||
* {@linkplain java.lang.foreign.Linker#upcallStub(java.lang.invoke.MethodHandle, FunctionDescriptor, SegmentScope) create}
|
||||
* a new upcall stub; the layouts in the function descriptors allow the linker to determine the sequence of steps which
|
||||
* allow foreign code to call the stub for {@code intCompareHandle} according to the rules specified by the ABI of the
|
||||
* underlying platform.
|
||||
* The lifecycle of the upcall stub is tied to the {@linkplain java.lang.foreign.SegmentScope scope}
|
||||
* provided when the upcall stub is created. This same scope is made available by the {@link java.lang.foreign.MemorySegment}
|
||||
* instance returned by that method.
|
||||
*
|
||||
* <h2 id="restricted">Restricted methods</h2>
|
||||
* Some methods in this package are considered <em>restricted</em>. Restricted methods are typically used to bind native
|
||||
* foreign data and/or functions to first-class Java API elements which can then be used directly by clients. For instance
|
||||
* the restricted method {@link java.lang.foreign.MemorySegment#ofAddress(long, long, SegmentScope)}
|
||||
* can be used to create a fresh segment with the given spatial bounds out of a native address.
|
||||
* the restricted method {@link java.lang.foreign.MemorySegment#reinterpret(long)} ()}
|
||||
* can be used to create a fresh segment with the same address and temporal bounds,
|
||||
* but with the provided size. This can be useful to resize memory segments obtained when interacting with native functions.
|
||||
* <p>
|
||||
* Binding foreign data and/or functions is generally unsafe and, if done incorrectly, can result in VM crashes,
|
||||
* or memory corruption when the bound Java API element is accessed. For instance, in the case of
|
||||
* {@link java.lang.foreign.MemorySegment#ofAddress(long, long, SegmentScope)}, if the provided spatial bounds are
|
||||
* incorrect, a client of the segment returned by that method might crash the VM, or corrupt
|
||||
* memory when attempting to access said segment. For these reasons, it is crucial for code that calls a restricted method
|
||||
* to never pass arguments that might cause incorrect binding of foreign data and/or functions to a Java API.
|
||||
* or memory corruption when the bound Java API element is accessed. For instance, incorrectly resizing a native
|
||||
* memory sgement using {@link java.lang.foreign.MemorySegment#reinterpret(long)} can lead to a JVM crash, or, worse,
|
||||
* lead to silent memory corruption when attempting to access the resized segment. For these reasons, it is crucial for
|
||||
* code that calls a restricted method to never pass arguments that might cause incorrect binding of foreign data and/or
|
||||
* functions to a Java API.
|
||||
* <p>
|
||||
* Given the potential danger of restricted methods, the Java runtime issues a warning on the standard error stream
|
||||
* every time a restricted method is invoked. Such warnings can be disabled by granting access to restricted methods
|
||||
|
||||
@ -815,7 +815,9 @@ public abstract sealed class Buffer
|
||||
|
||||
@Override
|
||||
public ByteBuffer newMappedByteBuffer(UnmapperProxy unmapperProxy, long address, int cap, Object obj, MemorySegment segment) {
|
||||
return new DirectByteBuffer(address, cap, obj, unmapperProxy.fileDescriptor(), unmapperProxy.isSync(), segment);
|
||||
return unmapperProxy == null
|
||||
? new DirectByteBuffer(address, cap, obj, segment)
|
||||
: new DirectByteBuffer(address, cap, obj, unmapperProxy.fileDescriptor(), unmapperProxy.isSync(), segment);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -26,8 +26,8 @@
|
||||
package java.nio.channels;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.spi.AbstractInterruptibleChannel;
|
||||
@ -1004,11 +1004,15 @@ public abstract class FileChannel
|
||||
|
||||
/**
|
||||
* Maps a region of this channel's file into a new mapped memory segment,
|
||||
* with the given offset, size and memory session.
|
||||
* with the given offset, size and arena.
|
||||
* The {@linkplain MemorySegment#address() address} of the returned memory
|
||||
* segment is the starting address of the mapped off-heap region backing
|
||||
* the segment.
|
||||
*
|
||||
* <p>
|
||||
* The lifetime of the returned segment is controlled by the provided arena.
|
||||
* For instance, if the provided arena is a closeable arena,
|
||||
* the returned segment will be unmapped when the provided closeable arena
|
||||
* is {@linkplain Arena#close() closed}.
|
||||
* <p> If the specified mapping mode is
|
||||
* {@linkplain FileChannel.MapMode#READ_ONLY READ_ONLY}, the resulting
|
||||
* segment will be read-only (see {@link MemorySegment#isReadOnly()}).
|
||||
@ -1049,8 +1053,8 @@ public abstract class FileChannel
|
||||
* The size (in bytes) of the mapped memory backing the memory
|
||||
* segment.
|
||||
*
|
||||
* @param session
|
||||
* The segment memory session.
|
||||
* @param arena
|
||||
* The segment arena.
|
||||
*
|
||||
* @return A new mapped memory segment.
|
||||
*
|
||||
@ -1059,13 +1063,11 @@ public abstract class FileChannel
|
||||
* {@code offset + size} overflows the range of {@code long}.
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
* If the {@code session} is not
|
||||
* {@linkplain SegmentScope#isAlive() alive}.
|
||||
* If {@code arena.isAlive() == false}.
|
||||
*
|
||||
* @throws WrongThreadException
|
||||
* If this method is called from a thread other than the thread
|
||||
* {@linkplain SegmentScope#isAccessibleBy(Thread) owning} the
|
||||
* {@code session}.
|
||||
* If {@code arena} is a confined scoped arena, and this method is called from a
|
||||
* thread {@code T}, other than the scoped arena's owner thread.
|
||||
*
|
||||
* @throws NonReadableChannelException
|
||||
* If the {@code mode} is {@link MapMode#READ_ONLY READ_ONLY} or
|
||||
@ -1087,7 +1089,7 @@ public abstract class FileChannel
|
||||
* @since 19
|
||||
*/
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
||||
public MemorySegment map(MapMode mode, long offset, long size, SegmentScope session)
|
||||
public MemorySegment map(MapMode mode, long offset, long size, Arena arena)
|
||||
throws IOException
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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,11 +25,7 @@
|
||||
|
||||
package jdk.internal.foreign;
|
||||
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentAllocator;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import java.lang.foreign.*;
|
||||
import java.lang.reflect.Array;
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
@ -47,11 +43,14 @@ import java.util.function.Function;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import jdk.internal.access.JavaNioAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.access.foreign.UnmapperProxy;
|
||||
import jdk.internal.misc.ScopedMemoryAccess;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
import jdk.internal.util.ArraysSupport;
|
||||
import jdk.internal.util.Preconditions;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
@ -77,16 +76,16 @@ public abstract sealed class AbstractMemorySegmentImpl
|
||||
|
||||
final long length;
|
||||
final boolean readOnly;
|
||||
final SegmentScope scope;
|
||||
final MemorySessionImpl scope;
|
||||
|
||||
@ForceInline
|
||||
AbstractMemorySegmentImpl(long length, boolean readOnly, SegmentScope scope) {
|
||||
AbstractMemorySegmentImpl(long length, boolean readOnly, MemorySessionImpl scope) {
|
||||
this.length = length;
|
||||
this.readOnly = readOnly;
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
abstract AbstractMemorySegmentImpl dup(long offset, long size, boolean readOnly, SegmentScope scope);
|
||||
abstract AbstractMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySessionImpl scope);
|
||||
|
||||
abstract ByteBuffer makeByteBuffer();
|
||||
|
||||
@ -112,6 +111,50 @@ public abstract sealed class AbstractMemorySegmentImpl
|
||||
return asSliceNoCheck(offset, length - offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment asSlice(long offset, long newSize, long byteAlignment) {
|
||||
checkBounds(offset, newSize);
|
||||
if (!isAlignedForElement(offset, byteAlignment)) {
|
||||
throw new IllegalArgumentException("Target offset incompatible with alignment constraints");
|
||||
}
|
||||
return asSliceNoCheck(offset, newSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CallerSensitive
|
||||
public final MemorySegment reinterpret(long newSize, Arena arena, Consumer<MemorySegment> cleanup) {
|
||||
Objects.requireNonNull(arena);
|
||||
return reinterpretInternal(Reflection.getCallerClass(), newSize,
|
||||
MemorySessionImpl.toMemorySession(arena), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CallerSensitive
|
||||
public final MemorySegment reinterpret(long newSize) {
|
||||
return reinterpretInternal(Reflection.getCallerClass(), newSize, scope, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CallerSensitive
|
||||
public final MemorySegment reinterpret(Arena arena, Consumer<MemorySegment> cleanup) {
|
||||
Objects.requireNonNull(arena);
|
||||
return reinterpretInternal(Reflection.getCallerClass(), byteSize(),
|
||||
MemorySessionImpl.toMemorySession(arena), cleanup);
|
||||
}
|
||||
|
||||
public MemorySegment reinterpretInternal(Class<?> callerClass, long newSize, Scope scope, Consumer<MemorySegment> cleanup) {
|
||||
Reflection.ensureNativeAccess(callerClass, MemorySegment.class, "reinterpret");
|
||||
if (newSize < 0) {
|
||||
throw new IllegalArgumentException("newSize < 0");
|
||||
}
|
||||
if (!isNative()) throw new UnsupportedOperationException("Not a native segment");
|
||||
Runnable action = cleanup != null ?
|
||||
() -> cleanup.accept(NativeMemorySegmentImpl.makeNativeSegmentUnchecked(address(), newSize)) :
|
||||
null;
|
||||
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(address(), newSize,
|
||||
(MemorySessionImpl)scope, action);
|
||||
}
|
||||
|
||||
private AbstractMemorySegmentImpl asSliceNoCheck(long offset, long newSize) {
|
||||
return dup(offset, newSize, readOnly, scope);
|
||||
}
|
||||
@ -148,7 +191,7 @@ public abstract sealed class AbstractMemorySegmentImpl
|
||||
@Override
|
||||
public MemorySegment allocate(long byteSize, long byteAlignment) {
|
||||
Utils.checkAllocationSizeAndAlign(byteSize, byteAlignment);
|
||||
return asSlice(0, byteSize);
|
||||
return asSlice(0, byteSize, byteAlignment);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -325,7 +368,12 @@ public abstract sealed class AbstractMemorySegmentImpl
|
||||
|
||||
@ForceInline
|
||||
public final boolean isAlignedForElement(long offset, MemoryLayout layout) {
|
||||
return (((unsafeGetOffset() + offset) | maxAlignMask()) & (layout.byteAlignment() - 1)) == 0;
|
||||
return isAlignedForElement(offset, layout.byteAlignment());
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public final boolean isAlignedForElement(long offset, long byteAlignment) {
|
||||
return (((unsafeGetOffset() + offset) | maxAlignMask()) & (byteAlignment - 1)) == 0;
|
||||
}
|
||||
|
||||
private int checkArraySize(String typeName, int elemSize) {
|
||||
@ -358,13 +406,18 @@ public abstract sealed class AbstractMemorySegmentImpl
|
||||
}
|
||||
|
||||
@Override
|
||||
public SegmentScope scope() {
|
||||
public Scope scope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccessibleBy(Thread thread) {
|
||||
return sessionImpl().isAccessibleBy(thread);
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public final MemorySessionImpl sessionImpl() {
|
||||
return (MemorySessionImpl)scope;
|
||||
return scope;
|
||||
}
|
||||
|
||||
private IndexOutOfBoundsException outOfBoundException(long offset, long length) {
|
||||
@ -450,7 +503,7 @@ public abstract sealed class AbstractMemorySegmentImpl
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MemorySegment{ array: " + array() + " address:" + address() + " limit: " + length + " }";
|
||||
return "MemorySegment{ heapBase: " + heapBase() + " address:" + address() + " limit: " + length + " }";
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -481,29 +534,29 @@ public abstract sealed class AbstractMemorySegmentImpl
|
||||
int size = limit - pos;
|
||||
|
||||
AbstractMemorySegmentImpl bufferSegment = (AbstractMemorySegmentImpl) NIO_ACCESS.bufferSegment(bb);
|
||||
final SegmentScope bufferScope;
|
||||
boolean readOnly = bb.isReadOnly();
|
||||
int scaleFactor = getScaleFactor(bb);
|
||||
final MemorySessionImpl bufferScope;
|
||||
if (bufferSegment != null) {
|
||||
bufferScope = bufferSegment.scope;
|
||||
} else {
|
||||
bufferScope = MemorySessionImpl.heapSession(bb);
|
||||
}
|
||||
boolean readOnly = bb.isReadOnly();
|
||||
int scaleFactor = getScaleFactor(bb);
|
||||
if (base != null) {
|
||||
if (base instanceof byte[]) {
|
||||
return new HeapMemorySegmentImpl.OfByte(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly);
|
||||
return new HeapMemorySegmentImpl.OfByte(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly, bufferScope);
|
||||
} else if (base instanceof short[]) {
|
||||
return new HeapMemorySegmentImpl.OfShort(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly);
|
||||
return new HeapMemorySegmentImpl.OfShort(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly, bufferScope);
|
||||
} else if (base instanceof char[]) {
|
||||
return new HeapMemorySegmentImpl.OfChar(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly);
|
||||
return new HeapMemorySegmentImpl.OfChar(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly, bufferScope);
|
||||
} else if (base instanceof int[]) {
|
||||
return new HeapMemorySegmentImpl.OfInt(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly);
|
||||
return new HeapMemorySegmentImpl.OfInt(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly, bufferScope);
|
||||
} else if (base instanceof float[]) {
|
||||
return new HeapMemorySegmentImpl.OfFloat(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly);
|
||||
return new HeapMemorySegmentImpl.OfFloat(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly, bufferScope);
|
||||
} else if (base instanceof long[]) {
|
||||
return new HeapMemorySegmentImpl.OfLong(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly);
|
||||
return new HeapMemorySegmentImpl.OfLong(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly, bufferScope);
|
||||
} else if (base instanceof double[]) {
|
||||
return new HeapMemorySegmentImpl.OfDouble(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly);
|
||||
return new HeapMemorySegmentImpl.OfDouble(bbAddress + (pos << scaleFactor), base, size << scaleFactor, readOnly, bufferScope);
|
||||
} else {
|
||||
throw new AssertionError("Cannot get here");
|
||||
}
|
||||
|
||||
@ -25,10 +25,13 @@
|
||||
*/
|
||||
package jdk.internal.foreign;
|
||||
|
||||
import jdk.internal.foreign.abi.fallback.FallbackLinker;
|
||||
import jdk.internal.vm.ForeignLinkerSupport;
|
||||
import jdk.internal.util.OperatingSystem;
|
||||
import jdk.internal.util.StaticProperty;
|
||||
|
||||
import static java.lang.foreign.ValueLayout.ADDRESS;
|
||||
import static sun.security.action.GetPropertyAction.privilegedGetProperty;
|
||||
|
||||
public enum CABI {
|
||||
SYS_V,
|
||||
@ -36,51 +39,52 @@ public enum CABI {
|
||||
LINUX_AARCH_64,
|
||||
MAC_OS_AARCH_64,
|
||||
WIN_AARCH_64,
|
||||
LINUX_RISCV_64;
|
||||
LINUX_RISCV_64,
|
||||
FALLBACK,
|
||||
UNSUPPORTED;
|
||||
|
||||
private static final CABI ABI;
|
||||
private static final String ARCH;
|
||||
private static final long ADDRESS_SIZE;
|
||||
private static final CABI CURRENT = computeCurrent();
|
||||
|
||||
static {
|
||||
ARCH = StaticProperty.osArch();
|
||||
ADDRESS_SIZE = ADDRESS.bitSize();
|
||||
// might be running in a 32-bit VM on a 64-bit platform.
|
||||
// addressSize will be correctly 32
|
||||
if ((ARCH.equals("amd64") || ARCH.equals("x86_64")) && ADDRESS_SIZE == 64) {
|
||||
if (OperatingSystem.isWindows()) {
|
||||
ABI = WIN_64;
|
||||
} else {
|
||||
ABI = SYS_V;
|
||||
}
|
||||
} else if (ARCH.equals("aarch64")) {
|
||||
if (OperatingSystem.isMacOS()) {
|
||||
ABI = MAC_OS_AARCH_64;
|
||||
} else if (OperatingSystem.isWindows()) {
|
||||
ABI = WIN_AARCH_64;
|
||||
} else {
|
||||
// The Linux ABI follows the standard AAPCS ABI
|
||||
ABI = LINUX_AARCH_64;
|
||||
}
|
||||
} else if (ARCH.equals("riscv64")) {
|
||||
if (OperatingSystem.isLinux()) {
|
||||
ABI = LINUX_RISCV_64;
|
||||
} else {
|
||||
// unsupported
|
||||
ABI = null;
|
||||
}
|
||||
} else {
|
||||
// unsupported
|
||||
ABI = null;
|
||||
private static CABI computeCurrent() {
|
||||
String abi = privilegedGetProperty("jdk.internal.foreign.CABI");
|
||||
if (abi != null) {
|
||||
return CABI.valueOf(abi);
|
||||
}
|
||||
|
||||
if (ForeignLinkerSupport.isSupported()) {
|
||||
// figure out the ABI based on the platform
|
||||
String arch = StaticProperty.osArch();
|
||||
long addressSize = ADDRESS.bitSize();
|
||||
// might be running in a 32-bit VM on a 64-bit platform.
|
||||
// addressSize will be correctly 32
|
||||
if ((arch.equals("amd64") || arch.equals("x86_64")) && addressSize == 64) {
|
||||
if (OperatingSystem.isWindows()) {
|
||||
return WIN_64;
|
||||
} else {
|
||||
return SYS_V;
|
||||
}
|
||||
} else if (arch.equals("aarch64")) {
|
||||
if (OperatingSystem.isMacOS()) {
|
||||
return MAC_OS_AARCH_64;
|
||||
} else if (OperatingSystem.isWindows()) {
|
||||
return WIN_AARCH_64;
|
||||
} else {
|
||||
// The Linux ABI follows the standard AAPCS ABI
|
||||
return LINUX_AARCH_64;
|
||||
}
|
||||
} else if (arch.equals("riscv64")) {
|
||||
if (OperatingSystem.isLinux()) {
|
||||
return LINUX_RISCV_64;
|
||||
}
|
||||
}
|
||||
} else if (FallbackLinker.isSupported()) {
|
||||
return FALLBACK; // fallback linker
|
||||
}
|
||||
|
||||
return UNSUPPORTED;
|
||||
}
|
||||
|
||||
public static CABI current() {
|
||||
if (ABI == null) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Unsupported os, arch, or address size: " + OperatingSystem.current() +
|
||||
", " + ARCH + ", " + ADDRESS_SIZE);
|
||||
}
|
||||
return ABI;
|
||||
return CURRENT;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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
|
||||
@ -27,7 +27,6 @@
|
||||
package jdk.internal.foreign;
|
||||
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
@ -46,7 +45,7 @@ import jdk.internal.vm.annotation.ForceInline;
|
||||
* the field type storing the 'base' coordinate is just Object; similarly, all the constructor in the subclasses
|
||||
* accept an Object 'base' parameter instead of a sharper type (e.g. {@code byte[]}). This is deliberate, as
|
||||
* using sharper types would require use of type-conversions, which in turn would inhibit some C2 optimizations,
|
||||
* such as the elimination of store barriers in methods like {@link HeapMemorySegmentImpl#dup(long, long, boolean, SegmentScope)}.
|
||||
* such as the elimination of store barriers in methods like {@link HeapMemorySegmentImpl#dup(long, long, boolean, MemorySessionImpl)}.
|
||||
*/
|
||||
public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegmentImpl {
|
||||
|
||||
@ -62,13 +61,15 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
|
||||
final Object base;
|
||||
|
||||
@Override
|
||||
public Optional<Object> array() {
|
||||
return Optional.of(base);
|
||||
public Optional<Object> heapBase() {
|
||||
return readOnly ?
|
||||
Optional.empty() :
|
||||
Optional.of(base);
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
HeapMemorySegmentImpl(long offset, Object base, long length, boolean readOnly) {
|
||||
super(length, readOnly, SegmentScope.global());
|
||||
HeapMemorySegmentImpl(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) {
|
||||
super(length, readOnly, session);
|
||||
this.offset = offset;
|
||||
this.base = base;
|
||||
}
|
||||
@ -79,28 +80,28 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
|
||||
}
|
||||
|
||||
@Override
|
||||
abstract HeapMemorySegmentImpl dup(long offset, long size, boolean readOnly, SegmentScope scope);
|
||||
abstract HeapMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySessionImpl scope);
|
||||
|
||||
@Override
|
||||
ByteBuffer makeByteBuffer() {
|
||||
if (!(base instanceof byte[])) {
|
||||
if (!(base instanceof byte[] baseByte)) {
|
||||
throw new UnsupportedOperationException("Not an address to an heap-allocated byte array");
|
||||
}
|
||||
JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
|
||||
return nioAccess.newHeapByteBuffer((byte[])base, (int)offset - BYTE_ARR_BASE, (int) byteSize(), null);
|
||||
return nioAccess.newHeapByteBuffer(baseByte, (int)offset - BYTE_ARR_BASE, (int) byteSize(), null);
|
||||
}
|
||||
|
||||
// factories
|
||||
|
||||
public static final class OfByte extends HeapMemorySegmentImpl {
|
||||
|
||||
OfByte(long offset, Object base, long length, boolean readOnly) {
|
||||
super(offset, base, length, readOnly);
|
||||
OfByte(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) {
|
||||
super(offset, base, length, readOnly, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
OfByte dup(long offset, long size, boolean readOnly, SegmentScope scope) {
|
||||
return new OfByte(this.offset + offset, base, size, readOnly);
|
||||
OfByte dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) {
|
||||
return new OfByte(this.offset + offset, base, size, readOnly, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -111,7 +112,8 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
|
||||
public static MemorySegment fromArray(byte[] arr) {
|
||||
Objects.requireNonNull(arr);
|
||||
long byteSize = (long)arr.length * Unsafe.ARRAY_BYTE_INDEX_SCALE;
|
||||
return new OfByte(Unsafe.ARRAY_BYTE_BASE_OFFSET, arr, byteSize, false);
|
||||
return new OfByte(Unsafe.ARRAY_BYTE_BASE_OFFSET, arr, byteSize, false,
|
||||
MemorySessionImpl.heapSession(arr));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -127,13 +129,13 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
|
||||
|
||||
public static final class OfChar extends HeapMemorySegmentImpl {
|
||||
|
||||
OfChar(long offset, Object base, long length, boolean readOnly) {
|
||||
super(offset, base, length, readOnly);
|
||||
OfChar(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) {
|
||||
super(offset, base, length, readOnly, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
OfChar dup(long offset, long size, boolean readOnly, SegmentScope scope) {
|
||||
return new OfChar(this.offset + offset, base, size, readOnly);
|
||||
OfChar dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) {
|
||||
return new OfChar(this.offset + offset, base, size, readOnly, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -144,7 +146,8 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
|
||||
public static MemorySegment fromArray(char[] arr) {
|
||||
Objects.requireNonNull(arr);
|
||||
long byteSize = (long)arr.length * Unsafe.ARRAY_CHAR_INDEX_SCALE;
|
||||
return new OfChar(Unsafe.ARRAY_CHAR_BASE_OFFSET, arr, byteSize, false);
|
||||
return new OfChar(Unsafe.ARRAY_CHAR_BASE_OFFSET, arr, byteSize, false,
|
||||
MemorySessionImpl.heapSession(arr));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -160,13 +163,13 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
|
||||
|
||||
public static final class OfShort extends HeapMemorySegmentImpl {
|
||||
|
||||
OfShort(long offset, Object base, long length, boolean readOnly) {
|
||||
super(offset, base, length, readOnly);
|
||||
OfShort(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) {
|
||||
super(offset, base, length, readOnly, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
OfShort dup(long offset, long size, boolean readOnly, SegmentScope scope) {
|
||||
return new OfShort(this.offset + offset, base, size, readOnly);
|
||||
OfShort dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) {
|
||||
return new OfShort(this.offset + offset, base, size, readOnly, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -177,7 +180,8 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
|
||||
public static MemorySegment fromArray(short[] arr) {
|
||||
Objects.requireNonNull(arr);
|
||||
long byteSize = (long)arr.length * Unsafe.ARRAY_SHORT_INDEX_SCALE;
|
||||
return new OfShort(Unsafe.ARRAY_SHORT_BASE_OFFSET, arr, byteSize, false);
|
||||
return new OfShort(Unsafe.ARRAY_SHORT_BASE_OFFSET, arr, byteSize, false,
|
||||
MemorySessionImpl.heapSession(arr));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -193,13 +197,13 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
|
||||
|
||||
public static final class OfInt extends HeapMemorySegmentImpl {
|
||||
|
||||
OfInt(long offset, Object base, long length, boolean readOnly) {
|
||||
super(offset, base, length, readOnly);
|
||||
OfInt(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) {
|
||||
super(offset, base, length, readOnly, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
OfInt dup(long offset, long size, boolean readOnly, SegmentScope scope) {
|
||||
return new OfInt(this.offset + offset, base, size, readOnly);
|
||||
OfInt dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) {
|
||||
return new OfInt(this.offset + offset, base, size, readOnly, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -210,7 +214,8 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
|
||||
public static MemorySegment fromArray(int[] arr) {
|
||||
Objects.requireNonNull(arr);
|
||||
long byteSize = (long)arr.length * Unsafe.ARRAY_INT_INDEX_SCALE;
|
||||
return new OfInt(Unsafe.ARRAY_INT_BASE_OFFSET, arr, byteSize, false);
|
||||
return new OfInt(Unsafe.ARRAY_INT_BASE_OFFSET, arr, byteSize, false,
|
||||
MemorySessionImpl.heapSession(arr));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -226,13 +231,13 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
|
||||
|
||||
public static final class OfLong extends HeapMemorySegmentImpl {
|
||||
|
||||
OfLong(long offset, Object base, long length, boolean readOnly) {
|
||||
super(offset, base, length, readOnly);
|
||||
OfLong(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) {
|
||||
super(offset, base, length, readOnly, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
OfLong dup(long offset, long size, boolean readOnly, SegmentScope scope) {
|
||||
return new OfLong(this.offset + offset, base, size, readOnly);
|
||||
OfLong dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) {
|
||||
return new OfLong(this.offset + offset, base, size, readOnly, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -243,7 +248,8 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
|
||||
public static MemorySegment fromArray(long[] arr) {
|
||||
Objects.requireNonNull(arr);
|
||||
long byteSize = (long)arr.length * Unsafe.ARRAY_LONG_INDEX_SCALE;
|
||||
return new OfLong(Unsafe.ARRAY_LONG_BASE_OFFSET, arr, byteSize, false);
|
||||
return new OfLong(Unsafe.ARRAY_LONG_BASE_OFFSET, arr, byteSize, false,
|
||||
MemorySessionImpl.heapSession(arr));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -259,13 +265,13 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
|
||||
|
||||
public static final class OfFloat extends HeapMemorySegmentImpl {
|
||||
|
||||
OfFloat(long offset, Object base, long length, boolean readOnly) {
|
||||
super(offset, base, length, readOnly);
|
||||
OfFloat(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) {
|
||||
super(offset, base, length, readOnly, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
OfFloat dup(long offset, long size, boolean readOnly, SegmentScope scope) {
|
||||
return new OfFloat(this.offset + offset, base, size, readOnly);
|
||||
OfFloat dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) {
|
||||
return new OfFloat(this.offset + offset, base, size, readOnly, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -276,7 +282,8 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
|
||||
public static MemorySegment fromArray(float[] arr) {
|
||||
Objects.requireNonNull(arr);
|
||||
long byteSize = (long)arr.length * Unsafe.ARRAY_FLOAT_INDEX_SCALE;
|
||||
return new OfFloat(Unsafe.ARRAY_FLOAT_BASE_OFFSET, arr, byteSize, false);
|
||||
return new OfFloat(Unsafe.ARRAY_FLOAT_BASE_OFFSET, arr, byteSize, false,
|
||||
MemorySessionImpl.heapSession(arr));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -292,13 +299,13 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
|
||||
|
||||
public static final class OfDouble extends HeapMemorySegmentImpl {
|
||||
|
||||
OfDouble(long offset, Object base, long length, boolean readOnly) {
|
||||
super(offset, base, length, readOnly);
|
||||
OfDouble(long offset, Object base, long length, boolean readOnly, MemorySessionImpl session) {
|
||||
super(offset, base, length, readOnly, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
OfDouble dup(long offset, long size, boolean readOnly, SegmentScope scope) {
|
||||
return new OfDouble(this.offset + offset, base, size, readOnly);
|
||||
OfDouble dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) {
|
||||
return new OfDouble(this.offset + offset, base, size, readOnly, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -309,7 +316,8 @@ public abstract sealed class HeapMemorySegmentImpl extends AbstractMemorySegment
|
||||
public static MemorySegment fromArray(double[] arr) {
|
||||
Objects.requireNonNull(arr);
|
||||
long byteSize = (long)arr.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE;
|
||||
return new OfDouble(Unsafe.ARRAY_DOUBLE_BASE_OFFSET, arr, byteSize, false);
|
||||
return new OfDouble(Unsafe.ARRAY_DOUBLE_BASE_OFFSET, arr, byteSize, false,
|
||||
MemorySessionImpl.heapSession(arr));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 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
|
||||
@ -27,6 +27,7 @@ package jdk.internal.foreign;
|
||||
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
|
||||
import java.lang.foreign.AddressLayout;
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
@ -53,9 +54,11 @@ public class LayoutPath {
|
||||
|
||||
private static final long[] EMPTY_STRIDES = new long[0];
|
||||
private static final long[] EMPTY_BOUNDS = new long[0];
|
||||
private static final MethodHandle[] EMPTY_DEREF_HANDLES = new MethodHandle[0];
|
||||
|
||||
private static final MethodHandle MH_ADD_SCALED_OFFSET;
|
||||
private static final MethodHandle MH_SLICE;
|
||||
private static final MethodHandle MH_SEGMENT_RESIZE;
|
||||
|
||||
static {
|
||||
try {
|
||||
@ -64,6 +67,8 @@ public class LayoutPath {
|
||||
MethodType.methodType(long.class, long.class, long.class, long.class, long.class));
|
||||
MH_SLICE = lookup.findVirtual(MemorySegment.class, "asSlice",
|
||||
MethodType.methodType(MemorySegment.class, long.class, long.class));
|
||||
MH_SEGMENT_RESIZE = lookup.findStatic(LayoutPath.class, "resizeSegment",
|
||||
MethodType.methodType(MemorySegment.class, MemorySegment.class, MemoryLayout.class));
|
||||
} catch (Throwable ex) {
|
||||
throw new ExceptionInInitializerError(ex);
|
||||
}
|
||||
@ -75,12 +80,14 @@ public class LayoutPath {
|
||||
private final long[] strides;
|
||||
|
||||
private final long[] bounds;
|
||||
private final MethodHandle[] derefAdapters;
|
||||
|
||||
private LayoutPath(MemoryLayout layout, long offset, long[] strides, long[] bounds, LayoutPath enclosing) {
|
||||
private LayoutPath(MemoryLayout layout, long offset, long[] strides, long[] bounds, MethodHandle[] derefAdapters, LayoutPath enclosing) {
|
||||
this.layout = layout;
|
||||
this.offset = offset;
|
||||
this.strides = strides;
|
||||
this.bounds = bounds;
|
||||
this.derefAdapters = derefAdapters;
|
||||
this.enclosing = enclosing;
|
||||
}
|
||||
|
||||
@ -90,7 +97,7 @@ public class LayoutPath {
|
||||
check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
|
||||
SequenceLayout seq = (SequenceLayout)layout;
|
||||
MemoryLayout elem = seq.elementLayout();
|
||||
return LayoutPath.nestedPath(elem, offset, addStride(elem.bitSize()), addBound(seq.elementCount()), this);
|
||||
return LayoutPath.nestedPath(elem, offset, addStride(elem.bitSize()), addBound(seq.elementCount()), derefAdapters, this);
|
||||
}
|
||||
|
||||
public LayoutPath sequenceElement(long start, long step) {
|
||||
@ -104,7 +111,7 @@ public class LayoutPath {
|
||||
start + 1;
|
||||
long maxIndex = Math.ceilDiv(nelems, Math.abs(step));
|
||||
return LayoutPath.nestedPath(elem, offset + (start * elemSize),
|
||||
addStride(elemSize * step), addBound(maxIndex), this);
|
||||
addStride(elemSize * step), addBound(maxIndex), derefAdapters, this);
|
||||
}
|
||||
|
||||
public LayoutPath sequenceElement(long index) {
|
||||
@ -113,7 +120,7 @@ public class LayoutPath {
|
||||
checkSequenceBounds(seq, index);
|
||||
long elemSize = seq.elementLayout().bitSize();
|
||||
long elemOffset = elemSize * index;
|
||||
return LayoutPath.nestedPath(seq.elementLayout(), offset + elemOffset, strides, bounds, this);
|
||||
return LayoutPath.nestedPath(seq.elementLayout(), offset + elemOffset, strides, bounds, derefAdapters,this);
|
||||
}
|
||||
|
||||
public LayoutPath groupElement(String name) {
|
||||
@ -134,7 +141,41 @@ public class LayoutPath {
|
||||
if (elem == null) {
|
||||
throw badLayoutPath("cannot resolve '" + name + "' in layout " + layout);
|
||||
}
|
||||
return LayoutPath.nestedPath(elem, this.offset + offset, strides, bounds, this);
|
||||
return LayoutPath.nestedPath(elem, this.offset + offset, strides, bounds, derefAdapters, this);
|
||||
}
|
||||
|
||||
public LayoutPath groupElement(long index) {
|
||||
check(GroupLayout.class, "attempting to select a group element from a non-group layout");
|
||||
GroupLayout g = (GroupLayout)layout;
|
||||
long elemSize = g.memberLayouts().size();
|
||||
long offset = 0;
|
||||
MemoryLayout elem = null;
|
||||
for (int i = 0; i <= index; i++) {
|
||||
if (i == elemSize) {
|
||||
throw badLayoutPath("cannot resolve element " + index + " in layout " + layout);
|
||||
}
|
||||
elem = g.memberLayouts().get(i);
|
||||
if (g instanceof StructLayout && i < index) {
|
||||
offset += elem.bitSize();
|
||||
}
|
||||
}
|
||||
return LayoutPath.nestedPath(elem, this.offset + offset, strides, bounds, derefAdapters, this);
|
||||
}
|
||||
|
||||
public LayoutPath derefElement() {
|
||||
if (!(layout instanceof AddressLayout addressLayout) ||
|
||||
addressLayout.targetLayout().isEmpty()) {
|
||||
throw badLayoutPath("Cannot dereference layout: " + layout);
|
||||
}
|
||||
MemoryLayout derefLayout = addressLayout.targetLayout().get();
|
||||
MethodHandle handle = dereferenceHandle(false).toMethodHandle(VarHandle.AccessMode.GET);
|
||||
handle = MethodHandles.filterReturnValue(handle,
|
||||
MethodHandles.insertArguments(MH_SEGMENT_RESIZE, 1, derefLayout));
|
||||
return derefPath(derefLayout, handle, this);
|
||||
}
|
||||
|
||||
private static MemorySegment resizeSegment(MemorySegment segment, MemoryLayout layout) {
|
||||
return Utils.longToAddress(segment.address(), layout.byteSize(), layout.byteAlignment());
|
||||
}
|
||||
|
||||
// Layout path projections
|
||||
@ -144,22 +185,31 @@ public class LayoutPath {
|
||||
}
|
||||
|
||||
public VarHandle dereferenceHandle() {
|
||||
return dereferenceHandle(true);
|
||||
}
|
||||
|
||||
public VarHandle dereferenceHandle(boolean adapt) {
|
||||
if (!(layout instanceof ValueLayout valueLayout)) {
|
||||
throw new IllegalArgumentException("Path does not select a value layout");
|
||||
}
|
||||
checkAlignment(this);
|
||||
|
||||
VarHandle handle = Utils.makeSegmentViewVarHandle(valueLayout);
|
||||
for (int i = strides.length - 1; i >= 0; i--) {
|
||||
MethodHandle collector = MethodHandles.insertArguments(MH_ADD_SCALED_OFFSET, 2,
|
||||
Utils.bitsToBytesOrThrow(strides[i], IllegalArgumentException::new),
|
||||
Utils.bitsToBytes(strides[i]),
|
||||
bounds[i]);
|
||||
// (J, ...) -> J to (J, J, ...) -> J
|
||||
// i.e. new coord is prefixed. Last coord will correspond to innermost layout
|
||||
handle = MethodHandles.collectCoordinates(handle, 1, collector);
|
||||
}
|
||||
handle = MethodHandles.insertCoordinates(handle, 1,
|
||||
Utils.bitsToBytesOrThrow(offset, IllegalArgumentException::new));
|
||||
Utils.bitsToBytes(offset));
|
||||
|
||||
if (adapt) {
|
||||
for (int i = derefAdapters.length; i > 0; i--) {
|
||||
handle = MethodHandles.collectCoordinates(handle, 0, derefAdapters[i - 1]);
|
||||
}
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
@ -182,13 +232,8 @@ public class LayoutPath {
|
||||
}
|
||||
|
||||
public MethodHandle sliceHandle() {
|
||||
if (strides.length == 0) {
|
||||
// trigger checks eagerly
|
||||
Utils.bitsToBytesOrThrow(offset, Utils.BITS_TO_BYTES_THROW_OFFSET);
|
||||
}
|
||||
|
||||
MethodHandle offsetHandle = offsetHandle(); // bit offset
|
||||
offsetHandle = MethodHandles.filterReturnValue(offsetHandle, Utils.MH_BITS_TO_BYTES_OR_THROW_FOR_OFFSET); // byte offset
|
||||
offsetHandle = MethodHandles.filterReturnValue(offsetHandle, Utils.BITS_TO_BYTES); // byte offset
|
||||
|
||||
MethodHandle sliceHandle = MH_SLICE; // (MS, long, long) -> MS
|
||||
sliceHandle = MethodHandles.insertArguments(sliceHandle, 2, layout.byteSize()); // (MS, long) -> MS
|
||||
@ -204,11 +249,17 @@ public class LayoutPath {
|
||||
// Layout path construction
|
||||
|
||||
public static LayoutPath rootPath(MemoryLayout layout) {
|
||||
return new LayoutPath(layout, 0L, EMPTY_STRIDES, EMPTY_BOUNDS, null);
|
||||
return new LayoutPath(layout, 0L, EMPTY_STRIDES, EMPTY_BOUNDS, EMPTY_DEREF_HANDLES, null);
|
||||
}
|
||||
|
||||
private static LayoutPath nestedPath(MemoryLayout layout, long offset, long[] strides, long[] bounds, LayoutPath encl) {
|
||||
return new LayoutPath(layout, offset, strides, bounds, encl);
|
||||
private static LayoutPath nestedPath(MemoryLayout layout, long offset, long[] strides, long[] bounds, MethodHandle[] derefAdapters, LayoutPath encl) {
|
||||
return new LayoutPath(layout, offset, strides, bounds, derefAdapters, encl);
|
||||
}
|
||||
|
||||
private static LayoutPath derefPath(MemoryLayout layout, MethodHandle handle, LayoutPath encl) {
|
||||
MethodHandle[] handles = Arrays.copyOf(encl.derefAdapters, encl.derefAdapters.length + 1);
|
||||
handles[encl.derefAdapters.length] = handle;
|
||||
return new LayoutPath(layout, 0L, EMPTY_STRIDES, EMPTY_BOUNDS, handles, null);
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
@ -229,26 +280,6 @@ public class LayoutPath {
|
||||
return new IllegalArgumentException("Bad layout path: " + cause);
|
||||
}
|
||||
|
||||
private static void checkAlignment(LayoutPath path) {
|
||||
MemoryLayout layout = path.layout;
|
||||
long alignment = layout.bitAlignment();
|
||||
if (!Utils.isAligned(path.offset, alignment)) {
|
||||
throw new UnsupportedOperationException("Invalid alignment requirements for layout " + layout);
|
||||
}
|
||||
for (long stride : path.strides) {
|
||||
if (!Utils.isAligned(stride, alignment)) {
|
||||
throw new UnsupportedOperationException("Alignment requirements for layout " + layout + " do not match stride " + stride);
|
||||
}
|
||||
}
|
||||
LayoutPath encl = path.enclosing;
|
||||
if (encl != null) {
|
||||
if (encl.layout.bitAlignment() < alignment) {
|
||||
throw new UnsupportedOperationException("Alignment requirements for layout " + layout + " do not match those for enclosing layout " + encl.layout);
|
||||
}
|
||||
checkAlignment(encl);
|
||||
}
|
||||
}
|
||||
|
||||
private long[] addStride(long stride) {
|
||||
long[] newStrides = Arrays.copyOf(strides, strides.length + 1);
|
||||
newStrides[strides.length] = stride;
|
||||
@ -271,7 +302,8 @@ public class LayoutPath {
|
||||
SEQUENCE_ELEMENT("unbound sequence element"),
|
||||
SEQUENCE_ELEMENT_INDEX("bound sequence element"),
|
||||
SEQUENCE_RANGE("sequence range"),
|
||||
GROUP_ELEMENT("group element");
|
||||
GROUP_ELEMENT("group element"),
|
||||
DEREF_ELEMENT("dereference element");
|
||||
|
||||
final String description;
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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,8 +25,6 @@
|
||||
|
||||
package jdk.internal.foreign;
|
||||
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.nio.ByteBuffer;
|
||||
import jdk.internal.access.foreign.UnmapperProxy;
|
||||
import jdk.internal.misc.ScopedMemoryAccess;
|
||||
@ -37,13 +35,13 @@ import jdk.internal.misc.ScopedMemoryAccess;
|
||||
* memory mapped segment, such as the file descriptor associated with the mapping. This information is crucial
|
||||
* in order to correctly reconstruct a byte buffer object from the segment (see {@link #makeByteBuffer()}).
|
||||
*/
|
||||
public sealed class MappedMemorySegmentImpl extends NativeMemorySegmentImpl {
|
||||
public final class MappedMemorySegmentImpl extends NativeMemorySegmentImpl {
|
||||
|
||||
private final UnmapperProxy unmapper;
|
||||
|
||||
static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess();
|
||||
|
||||
public MappedMemorySegmentImpl(long min, UnmapperProxy unmapper, long length, boolean readOnly, SegmentScope scope) {
|
||||
public MappedMemorySegmentImpl(long min, UnmapperProxy unmapper, long length, boolean readOnly, MemorySessionImpl scope) {
|
||||
super(min, length, readOnly, scope);
|
||||
this.unmapper = unmapper;
|
||||
}
|
||||
@ -55,7 +53,7 @@ public sealed class MappedMemorySegmentImpl extends NativeMemorySegmentImpl {
|
||||
}
|
||||
|
||||
@Override
|
||||
MappedMemorySegmentImpl dup(long offset, long size, boolean readOnly, SegmentScope scope) {
|
||||
MappedMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) {
|
||||
return new MappedMemorySegmentImpl(min + offset, unmapper, size, readOnly, scope);
|
||||
}
|
||||
|
||||
@ -73,50 +71,26 @@ public sealed class MappedMemorySegmentImpl extends NativeMemorySegmentImpl {
|
||||
|
||||
// support for mapped segments
|
||||
|
||||
public MemorySegment segment() {
|
||||
return MappedMemorySegmentImpl.this;
|
||||
}
|
||||
|
||||
public void load() {
|
||||
SCOPED_MEMORY_ACCESS.load(sessionImpl(), min, unmapper.isSync(), length);
|
||||
if (unmapper != null) {
|
||||
SCOPED_MEMORY_ACCESS.load(sessionImpl(), min, unmapper.isSync(), length);
|
||||
}
|
||||
}
|
||||
|
||||
public void unload() {
|
||||
SCOPED_MEMORY_ACCESS.unload(sessionImpl(), min, unmapper.isSync(), length);
|
||||
if (unmapper != null) {
|
||||
SCOPED_MEMORY_ACCESS.unload(sessionImpl(), min, unmapper.isSync(), length);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isLoaded() {
|
||||
return SCOPED_MEMORY_ACCESS.isLoaded(sessionImpl(), min, unmapper.isSync(), length);
|
||||
return unmapper == null || SCOPED_MEMORY_ACCESS.isLoaded(sessionImpl(), min, unmapper.isSync(), length);
|
||||
}
|
||||
|
||||
public void force() {
|
||||
SCOPED_MEMORY_ACCESS.force(sessionImpl(), unmapper.fileDescriptor(), min, unmapper.isSync(), 0, length);
|
||||
}
|
||||
|
||||
public static final class EmptyMappedMemorySegmentImpl extends MappedMemorySegmentImpl {
|
||||
|
||||
public EmptyMappedMemorySegmentImpl(boolean readOnly, MemorySessionImpl session) {
|
||||
super(0, null, 0, readOnly, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoaded() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void force() {
|
||||
// do nothing
|
||||
if (unmapper != null) {
|
||||
SCOPED_MEMORY_ACCESS.force(sessionImpl(), unmapper.fileDescriptor(), min, unmapper.isSync(), 0, length);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -26,10 +26,9 @@
|
||||
|
||||
package jdk.internal.foreign;
|
||||
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.SegmentAllocator;
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.MemorySegment.Scope;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.lang.ref.Cleaner;
|
||||
@ -51,7 +50,7 @@ import jdk.internal.vm.annotation.ForceInline;
|
||||
* access is possible when a session is being closed (see {@link jdk.internal.misc.ScopedMemoryAccess}).
|
||||
*/
|
||||
public abstract sealed class MemorySessionImpl
|
||||
implements SegmentScope, SegmentAllocator
|
||||
implements Scope
|
||||
permits ConfinedSession, GlobalSession, SharedSession {
|
||||
static final int OPEN = 0;
|
||||
static final int CLOSING = -1;
|
||||
@ -80,7 +79,7 @@ public abstract sealed class MemorySessionImpl
|
||||
public Arena asArena() {
|
||||
return new Arena() {
|
||||
@Override
|
||||
public SegmentScope scope() {
|
||||
public Scope scope() {
|
||||
return MemorySessionImpl.this;
|
||||
}
|
||||
|
||||
@ -88,16 +87,20 @@ public abstract sealed class MemorySessionImpl
|
||||
public void close() {
|
||||
MemorySessionImpl.this.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCloseableBy(Thread thread) {
|
||||
Objects.requireNonNull(thread);
|
||||
return ownerThread() == null || // shared
|
||||
ownerThread() == thread;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public static final MemorySessionImpl toMemorySession(Arena arena) {
|
||||
return (MemorySessionImpl) arena.scope();
|
||||
}
|
||||
|
||||
public final boolean isCloseableBy(Thread thread) {
|
||||
Objects.requireNonNull(thread);
|
||||
return isCloseable() &&
|
||||
(owner == null || owner == thread);
|
||||
}
|
||||
|
||||
public void addCloseAction(Runnable runnable) {
|
||||
Objects.requireNonNull(runnable);
|
||||
addInternal(ResourceList.ResourceCleanup.ofRunnable(runnable));
|
||||
@ -150,7 +153,6 @@ public abstract sealed class MemorySessionImpl
|
||||
return new ImplicitSession(cleaner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment allocate(long byteSize, long byteAlignment) {
|
||||
Utils.checkAllocationSizeAndAlign(byteSize, byteAlignment);
|
||||
return NativeMemorySegmentImpl.makeNativeSegment(byteSize, byteAlignment, this);
|
||||
@ -160,7 +162,6 @@ public abstract sealed class MemorySessionImpl
|
||||
|
||||
public abstract void acquire0();
|
||||
|
||||
@Override
|
||||
public void whileAlive(Runnable action) {
|
||||
Objects.requireNonNull(action);
|
||||
acquire0();
|
||||
@ -175,12 +176,6 @@ public abstract sealed class MemorySessionImpl
|
||||
return owner;
|
||||
}
|
||||
|
||||
public static boolean sameOwnerThread(SegmentScope scope1, SegmentScope scope2) {
|
||||
return ((MemorySessionImpl) scope1).ownerThread() ==
|
||||
((MemorySessionImpl) scope2).ownerThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isAccessibleBy(Thread thread) {
|
||||
Objects.requireNonNull(thread);
|
||||
return owner == null || owner == thread;
|
||||
@ -225,6 +220,10 @@ public abstract sealed class MemorySessionImpl
|
||||
}
|
||||
}
|
||||
|
||||
public static final void checkValidState(MemorySegment segment) {
|
||||
((AbstractMemorySegmentImpl)segment).sessionImpl().checkValidState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object clone() throws CloneNotSupportedException {
|
||||
throw new CloneNotSupportedException();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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
|
||||
@ -27,7 +27,6 @@
|
||||
package jdk.internal.foreign;
|
||||
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Optional;
|
||||
|
||||
@ -52,9 +51,24 @@ public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl pe
|
||||
final long min;
|
||||
|
||||
@ForceInline
|
||||
NativeMemorySegmentImpl(long min, long length, boolean readOnly, SegmentScope scope) {
|
||||
NativeMemorySegmentImpl(long min, long length, boolean readOnly, MemorySessionImpl scope) {
|
||||
super(length, readOnly, scope);
|
||||
this.min = min;
|
||||
this.min = (Unsafe.getUnsafe().addressSize() == 4)
|
||||
// On 32-bit systems, normalize the upper unused 32-bits to zero
|
||||
? min & 0x0000_0000_FFFF_FFFFL
|
||||
// On 64-bit systems, all the bits are used
|
||||
: min;
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor should only be used when initializing {@link MemorySegment#NULL}. Note: because of the memory
|
||||
* segment class hierarchy, it is possible to end up in a situation where this constructor is called
|
||||
* when the static fields in this class are not yet initialized.
|
||||
*/
|
||||
@ForceInline
|
||||
public NativeMemorySegmentImpl() {
|
||||
super(0L, false, new GlobalSession(null));
|
||||
this.min = 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -63,13 +77,13 @@ public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl pe
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Object> array() {
|
||||
public Optional<Object> heapBase() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
@Override
|
||||
NativeMemorySegmentImpl dup(long offset, long size, boolean readOnly, SegmentScope scope) {
|
||||
NativeMemorySegmentImpl dup(long offset, long size, boolean readOnly, MemorySessionImpl scope) {
|
||||
return new NativeMemorySegmentImpl(min + offset, size, readOnly, scope);
|
||||
}
|
||||
|
||||
@ -101,8 +115,7 @@ public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl pe
|
||||
|
||||
// factories
|
||||
|
||||
public static MemorySegment makeNativeSegment(long byteSize, long byteAlignment, SegmentScope scope) {
|
||||
MemorySessionImpl sessionImpl = (MemorySessionImpl) scope;
|
||||
public static MemorySegment makeNativeSegment(long byteSize, long byteAlignment, MemorySessionImpl sessionImpl) {
|
||||
sessionImpl.checkValidState();
|
||||
if (VM.isDirectMemoryPageAligned()) {
|
||||
byteAlignment = Math.max(byteAlignment, NIO_ACCESS.pageSize());
|
||||
@ -119,7 +132,7 @@ public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl pe
|
||||
}
|
||||
long alignedBuf = Utils.alignUp(buf, byteAlignment);
|
||||
AbstractMemorySegmentImpl segment = new NativeMemorySegmentImpl(buf, alignedSize,
|
||||
false, scope);
|
||||
false, sessionImpl);
|
||||
sessionImpl.addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() {
|
||||
@Override
|
||||
public void cleanup() {
|
||||
@ -138,25 +151,23 @@ public sealed class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl pe
|
||||
// associated with MemorySegment::ofAddress.
|
||||
|
||||
@ForceInline
|
||||
public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, SegmentScope scope, Runnable action) {
|
||||
MemorySessionImpl sessionImpl = (MemorySessionImpl) scope;
|
||||
public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, MemorySessionImpl sessionImpl, Runnable action) {
|
||||
if (action == null) {
|
||||
sessionImpl.checkValidState();
|
||||
} else {
|
||||
sessionImpl.addCloseAction(action);
|
||||
}
|
||||
return new NativeMemorySegmentImpl(min, byteSize, false, scope);
|
||||
return new NativeMemorySegmentImpl(min, byteSize, false, sessionImpl);
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, SegmentScope scope) {
|
||||
MemorySessionImpl sessionImpl = (MemorySessionImpl) scope;
|
||||
public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize, MemorySessionImpl sessionImpl) {
|
||||
sessionImpl.checkValidState();
|
||||
return new NativeMemorySegmentImpl(min, byteSize, false, scope);
|
||||
return new NativeMemorySegmentImpl(min, byteSize, false, sessionImpl);
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public static MemorySegment makeNativeSegmentUnchecked(long min, long byteSize) {
|
||||
return new NativeMemorySegmentImpl(min, byteSize, false, SegmentScope.global());
|
||||
return new NativeMemorySegmentImpl(min, byteSize, false, new GlobalSession(null));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 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
|
||||
@ -43,14 +43,14 @@ public final class SlicingAllocator implements SegmentAllocator {
|
||||
MemorySegment trySlice(long byteSize, long byteAlignment) {
|
||||
long min = segment.address();
|
||||
long start = Utils.alignUp(min + sp, byteAlignment) - min;
|
||||
MemorySegment slice = segment.asSlice(start, byteSize);
|
||||
MemorySegment slice = segment.asSlice(start, byteSize, byteAlignment);
|
||||
sp = start + byteSize;
|
||||
return slice;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment allocate(long byteSize, long byteAlignment) {
|
||||
Utils.checkAllocationSizeAndAlign(byteSize, byteAlignment, maxAlign);
|
||||
Utils.checkAllocationSizeAndAlign(byteSize, byteAlignment);
|
||||
// try to slice from current segment first...
|
||||
return trySlice(byteSize, byteAlignment);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 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,9 +25,7 @@
|
||||
|
||||
package jdk.internal.foreign;
|
||||
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.SymbolLookup;
|
||||
import java.lang.foreign.*;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@ -47,7 +45,10 @@ public final class SystemLookup implements SymbolLookup {
|
||||
private static final SystemLookup INSTANCE = new SystemLookup();
|
||||
|
||||
/* A fallback lookup, used when creation of system lookup fails. */
|
||||
private static final SymbolLookup FALLBACK_LOOKUP = name -> Optional.empty();
|
||||
private static final SymbolLookup FALLBACK_LOOKUP = name -> {
|
||||
Objects.requireNonNull(name);
|
||||
return Optional.empty();
|
||||
};
|
||||
|
||||
/*
|
||||
* On POSIX systems, dlsym will allow us to lookup symbol in library dependencies; the same trick doesn't work
|
||||
@ -57,10 +58,11 @@ public final class SystemLookup implements SymbolLookup {
|
||||
|
||||
private static SymbolLookup makeSystemLookup() {
|
||||
try {
|
||||
return switch (CABI.current()) {
|
||||
case SYS_V, LINUX_AARCH_64, MAC_OS_AARCH_64, LINUX_RISCV_64 -> libLookup(libs -> libs.load(jdkLibraryPath("syslookup")));
|
||||
case WIN_64, WIN_AARCH_64 -> makeWindowsLookup(); // out of line to workaround javac crash
|
||||
};
|
||||
if (Utils.IS_WINDOWS) {
|
||||
return makeWindowsLookup();
|
||||
} else {
|
||||
return libLookup(libs -> libs.load(jdkLibraryPath("syslookup")));
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
// This can happen in the event of a library loading failure - e.g. if one of the libraries the
|
||||
// system lookup depends on cannot be loaded for some reason. In such extreme cases, rather than
|
||||
@ -84,15 +86,17 @@ public final class SystemLookup implements SymbolLookup {
|
||||
SymbolLookup fallbackLibLookup =
|
||||
libLookup(libs -> libs.load(jdkLibraryPath("syslookup")));
|
||||
|
||||
int numSymbols = WindowsFallbackSymbols.values().length;
|
||||
MemorySegment funcs = MemorySegment.ofAddress(fallbackLibLookup.find("funcs").orElseThrow().address(),
|
||||
ADDRESS.byteSize() * numSymbols, SegmentScope.global());
|
||||
MemorySegment funcs = fallbackLibLookup.find("funcs").orElseThrow()
|
||||
.reinterpret(WindowsFallbackSymbols.LAYOUT.byteSize());
|
||||
|
||||
Function<String, Optional<MemorySegment>> fallbackLookup = name -> Optional.ofNullable(WindowsFallbackSymbols.valueOfOrNull(name))
|
||||
.map(symbol -> MemorySegment.ofAddress(funcs.getAtIndex(ADDRESS, symbol.ordinal()).address(), 0L, SegmentScope.global()));
|
||||
.map(symbol -> funcs.getAtIndex(ADDRESS, symbol.ordinal()));
|
||||
|
||||
final SymbolLookup finalLookup = lookup;
|
||||
lookup = name -> finalLookup.find(name).or(() -> fallbackLookup.apply(name));
|
||||
lookup = name -> {
|
||||
Objects.requireNonNull(name);
|
||||
return finalLookup.find(name).or(() -> fallbackLookup.apply(name));
|
||||
};
|
||||
}
|
||||
|
||||
return lookup;
|
||||
@ -106,7 +110,7 @@ public final class SystemLookup implements SymbolLookup {
|
||||
long addr = lib.lookup(name);
|
||||
return addr == 0 ?
|
||||
Optional.empty() :
|
||||
Optional.of(MemorySegment.ofAddress(addr, 0, SegmentScope.global()));
|
||||
Optional.of(MemorySegment.ofAddress(addr));
|
||||
} catch (NoSuchMethodException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@ -118,10 +122,7 @@ public final class SystemLookup implements SymbolLookup {
|
||||
*/
|
||||
private static Path jdkLibraryPath(String name) {
|
||||
Path javahome = Path.of(GetPropertyAction.privilegedGetProperty("java.home"));
|
||||
String lib = switch (CABI.current()) {
|
||||
case SYS_V, LINUX_AARCH_64, MAC_OS_AARCH_64, LINUX_RISCV_64 -> "lib";
|
||||
case WIN_64, WIN_AARCH_64 -> "bin";
|
||||
};
|
||||
String lib = Utils.IS_WINDOWS ? "bin" : "lib";
|
||||
String libname = System.mapLibraryName(name);
|
||||
return javahome.resolve(lib).resolve(libname);
|
||||
}
|
||||
@ -202,5 +203,8 @@ public final class SystemLookup implements SymbolLookup {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static final SequenceLayout LAYOUT = MemoryLayout.sequenceLayout(
|
||||
values().length, ADDRESS);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 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,6 +26,7 @@
|
||||
|
||||
package jdk.internal.foreign;
|
||||
|
||||
import java.lang.foreign.AddressLayout;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentAllocator;
|
||||
@ -39,27 +40,30 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
||||
import static java.lang.foreign.ValueLayout.JAVA_BYTE;
|
||||
import static sun.security.action.GetPropertyAction.privilegedGetProperty;
|
||||
|
||||
/**
|
||||
* This class contains misc helper functions to support creation of memory segments.
|
||||
*/
|
||||
public final class Utils {
|
||||
|
||||
public static final boolean IS_WINDOWS = privilegedGetProperty("os.name").startsWith("Windows");
|
||||
|
||||
// Suppresses default constructor, ensuring non-instantiability.
|
||||
private Utils() {}
|
||||
|
||||
private static final MethodHandle BYTE_TO_BOOL;
|
||||
private static final MethodHandle BOOL_TO_BYTE;
|
||||
private static final MethodHandle ADDRESS_TO_LONG;
|
||||
private static final MethodHandle LONG_TO_ADDRESS_SAFE;
|
||||
private static final MethodHandle LONG_TO_ADDRESS_UNSAFE;
|
||||
public static final MethodHandle MH_BITS_TO_BYTES_OR_THROW_FOR_OFFSET;
|
||||
|
||||
public static final Supplier<RuntimeException> BITS_TO_BYTES_THROW_OFFSET
|
||||
= () -> new UnsupportedOperationException("Cannot compute byte offset; bit offset is not a multiple of 8");
|
||||
private static final MethodHandle LONG_TO_ADDRESS;
|
||||
public static final MethodHandle BITS_TO_BYTES;
|
||||
|
||||
static {
|
||||
try {
|
||||
@ -70,15 +74,10 @@ public final class Utils {
|
||||
MethodType.methodType(byte.class, boolean.class));
|
||||
ADDRESS_TO_LONG = lookup.findStatic(SharedUtils.class, "unboxSegment",
|
||||
MethodType.methodType(long.class, MemorySegment.class));
|
||||
LONG_TO_ADDRESS_SAFE = lookup.findStatic(Utils.class, "longToAddressSafe",
|
||||
MethodType.methodType(MemorySegment.class, long.class));
|
||||
LONG_TO_ADDRESS_UNSAFE = lookup.findStatic(Utils.class, "longToAddressUnsafe",
|
||||
MethodType.methodType(MemorySegment.class, long.class));
|
||||
MH_BITS_TO_BYTES_OR_THROW_FOR_OFFSET = MethodHandles.insertArguments(
|
||||
lookup.findStatic(Utils.class, "bitsToBytesOrThrow",
|
||||
MethodType.methodType(long.class, long.class, Supplier.class)),
|
||||
1,
|
||||
BITS_TO_BYTES_THROW_OFFSET);
|
||||
LONG_TO_ADDRESS = lookup.findStatic(Utils.class, "longToAddress",
|
||||
MethodType.methodType(MemorySegment.class, long.class, long.class, long.class));
|
||||
BITS_TO_BYTES = lookup.findStatic(Utils.class, "bitsToBytes",
|
||||
MethodType.methodType(long.class, long.class));
|
||||
} catch (Throwable ex) {
|
||||
throw new ExceptionInInitializerError(ex);
|
||||
}
|
||||
@ -93,28 +92,25 @@ public final class Utils {
|
||||
return ms.asSlice(alignUp(offset, alignment) - offset);
|
||||
}
|
||||
|
||||
public static long bitsToBytesOrThrow(long bits, Supplier<RuntimeException> exFactory) {
|
||||
if (Utils.isAligned(bits, 8)) {
|
||||
return bits / 8;
|
||||
} else {
|
||||
throw exFactory.get();
|
||||
}
|
||||
public static long bitsToBytes(long bits) {
|
||||
assert Utils.isAligned(bits, 8);
|
||||
return bits / Byte.SIZE;
|
||||
}
|
||||
|
||||
public static VarHandle makeSegmentViewVarHandle(ValueLayout layout) {
|
||||
class VarHandleCache {
|
||||
private static final Map<ValueLayout, VarHandle> handleMap = new ConcurrentHashMap<>();
|
||||
final class VarHandleCache {
|
||||
private static final Map<ValueLayout, VarHandle> HANDLE_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
static VarHandle put(ValueLayout layout, VarHandle handle) {
|
||||
VarHandle prev = handleMap.putIfAbsent(layout, handle);
|
||||
VarHandle prev = HANDLE_MAP.putIfAbsent(layout, handle);
|
||||
return prev != null ? prev : handle;
|
||||
}
|
||||
}
|
||||
Class<?> baseCarrier = layout.carrier();
|
||||
if (layout.carrier() == MemorySegment.class) {
|
||||
baseCarrier = switch ((int) ValueLayout.ADDRESS.byteSize()) {
|
||||
case 8 -> long.class;
|
||||
case 4 -> int.class;
|
||||
case Long.BYTES -> long.class;
|
||||
case Integer.BYTES -> int.class;
|
||||
default -> throw new UnsupportedOperationException("Unsupported address layout");
|
||||
};
|
||||
} else if (layout.carrier() == boolean.class) {
|
||||
@ -126,11 +122,12 @@ public final class Utils {
|
||||
|
||||
if (layout.carrier() == boolean.class) {
|
||||
handle = MethodHandles.filterValue(handle, BOOL_TO_BYTE, BYTE_TO_BOOL);
|
||||
} else if (layout instanceof ValueLayout.OfAddress addressLayout) {
|
||||
} else if (layout instanceof AddressLayout addressLayout) {
|
||||
handle = MethodHandles.filterValue(handle,
|
||||
MethodHandles.explicitCastArguments(ADDRESS_TO_LONG, MethodType.methodType(baseCarrier, MemorySegment.class)),
|
||||
MethodHandles.explicitCastArguments(addressLayout.isUnbounded() ?
|
||||
LONG_TO_ADDRESS_UNSAFE : LONG_TO_ADDRESS_SAFE, MethodType.methodType(MemorySegment.class, baseCarrier)));
|
||||
MethodHandles.explicitCastArguments(MethodHandles.insertArguments(LONG_TO_ADDRESS, 1,
|
||||
pointeeByteSize(addressLayout), pointeeByteAlign(addressLayout)),
|
||||
MethodType.methodType(MemorySegment.class, baseCarrier)));
|
||||
}
|
||||
return VarHandleCache.put(layout, handle);
|
||||
}
|
||||
@ -144,13 +141,19 @@ public final class Utils {
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
private static MemorySegment longToAddressSafe(long addr) {
|
||||
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, 0);
|
||||
public static MemorySegment longToAddress(long addr, long size, long align) {
|
||||
if (!isAligned(addr, align)) {
|
||||
throw new IllegalArgumentException("Invalid alignment constraint for address: " + addr);
|
||||
}
|
||||
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, size);
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
private static MemorySegment longToAddressUnsafe(long addr) {
|
||||
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, Long.MAX_VALUE);
|
||||
public static MemorySegment longToAddress(long addr, long size, long align, MemorySessionImpl scope) {
|
||||
if (!isAligned(addr, align)) {
|
||||
throw new IllegalArgumentException("Invalid alignment constraint for address: " + addr);
|
||||
}
|
||||
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, size, scope);
|
||||
}
|
||||
|
||||
public static void copy(MemorySegment addr, byte[] bytes) {
|
||||
@ -172,24 +175,21 @@ public final class Utils {
|
||||
|
||||
@ForceInline
|
||||
public static void checkElementAlignment(MemoryLayout layout, String msg) {
|
||||
if (layout.bitAlignment() > layout.bitSize()) {
|
||||
if (layout.byteAlignment() > layout.byteSize()) {
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static long pointeeSize(MemoryLayout layout) {
|
||||
if (layout instanceof ValueLayout.OfAddress addressLayout) {
|
||||
return addressLayout.isUnbounded() ? Long.MAX_VALUE : 0L;
|
||||
} else {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
public static long pointeeByteSize(AddressLayout addressLayout) {
|
||||
return addressLayout.targetLayout()
|
||||
.map(MemoryLayout::byteSize)
|
||||
.orElse(0L);
|
||||
}
|
||||
|
||||
public static void checkAllocationSizeAndAlign(long byteSize, long byteAlignment, long maxAlignment) {
|
||||
checkAllocationSizeAndAlign(byteSize, byteAlignment);
|
||||
if (maxAlignment != 0 && byteAlignment > maxAlignment) {
|
||||
throw new IllegalArgumentException("Invalid alignment constraint : " + byteAlignment + " > " + maxAlignment);
|
||||
}
|
||||
public static long pointeeByteAlign(AddressLayout addressLayout) {
|
||||
return addressLayout.targetLayout()
|
||||
.map(MemoryLayout::byteAlignment)
|
||||
.orElse(1L);
|
||||
}
|
||||
|
||||
public static void checkAllocationSizeAndAlign(long byteSize, long byteAlignment) {
|
||||
|
||||
@ -28,6 +28,7 @@ import jdk.internal.foreign.SystemLookup;
|
||||
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.riscv64.linux.LinuxRISCV64Linker;
|
||||
import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
|
||||
import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
|
||||
@ -35,7 +36,7 @@ import jdk.internal.foreign.layout.AbstractLayout;
|
||||
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.Linker;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
@ -46,15 +47,16 @@ import java.util.Objects;
|
||||
|
||||
public abstract sealed class AbstractLinker implements Linker permits LinuxAArch64Linker, MacOsAArch64Linker,
|
||||
SysVx64Linker, WindowsAArch64Linker,
|
||||
Windowsx64Linker, LinuxRISCV64Linker {
|
||||
Windowsx64Linker, LinuxRISCV64Linker,
|
||||
FallbackLinker {
|
||||
|
||||
public interface UpcallStubFactory {
|
||||
MemorySegment makeStub(MethodHandle target, SegmentScope arena);
|
||||
MemorySegment makeStub(MethodHandle target, Arena arena);
|
||||
}
|
||||
|
||||
private record LinkRequest(FunctionDescriptor descriptor, LinkerOptions options) {}
|
||||
private final SoftReferenceCache<LinkRequest, MethodHandle> DOWNCALL_CACHE = new SoftReferenceCache<>();
|
||||
private final SoftReferenceCache<FunctionDescriptor, UpcallStubFactory> UPCALL_CACHE = new SoftReferenceCache<>();
|
||||
private final SoftReferenceCache<LinkRequest, UpcallStubFactory> UPCALL_CACHE = new SoftReferenceCache<>();
|
||||
|
||||
@Override
|
||||
public MethodHandle downcallHandle(FunctionDescriptor function, Option... options) {
|
||||
@ -74,23 +76,25 @@ public abstract sealed class AbstractLinker implements Linker permits LinuxAArch
|
||||
protected abstract MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function, LinkerOptions options);
|
||||
|
||||
@Override
|
||||
public MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, SegmentScope scope) {
|
||||
Objects.requireNonNull(scope);
|
||||
public MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, Arena arena, Linker.Option... options) {
|
||||
Objects.requireNonNull(arena);
|
||||
Objects.requireNonNull(target);
|
||||
Objects.requireNonNull(function);
|
||||
checkHasNaturalAlignment(function);
|
||||
SharedUtils.checkExceptions(target);
|
||||
LinkerOptions optionSet = LinkerOptions.forUpcall(function, options);
|
||||
|
||||
MethodType type = function.toMethodType();
|
||||
if (!type.equals(target.type())) {
|
||||
throw new IllegalArgumentException("Wrong method handle type: " + target.type());
|
||||
}
|
||||
|
||||
UpcallStubFactory factory = UPCALL_CACHE.get(function, f -> arrangeUpcall(type, f));
|
||||
return factory.makeStub(target, scope);
|
||||
UpcallStubFactory factory = UPCALL_CACHE.get(new LinkRequest(function, optionSet), linkRequest ->
|
||||
arrangeUpcall(type, linkRequest.descriptor(), linkRequest.options()));
|
||||
return factory.makeStub(target, arena);
|
||||
}
|
||||
|
||||
protected abstract UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function);
|
||||
protected abstract UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options);
|
||||
|
||||
@Override
|
||||
public SystemLookup defaultLookup() {
|
||||
|
||||
@ -24,14 +24,11 @@
|
||||
*/
|
||||
package jdk.internal.foreign.abi;
|
||||
|
||||
import jdk.internal.foreign.NativeMemorySegmentImpl;
|
||||
import jdk.internal.foreign.Utils;
|
||||
import jdk.internal.foreign.abi.BindingInterpreter.LoadFunc;
|
||||
import jdk.internal.foreign.abi.BindingInterpreter.StoreFunc;
|
||||
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.SegmentAllocator;
|
||||
import java.lang.foreign.*;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
@ -199,103 +196,10 @@ import static java.lang.foreign.ValueLayout.JAVA_SHORT_UNALIGNED;
|
||||
*/
|
||||
public sealed interface Binding {
|
||||
|
||||
/**
|
||||
* A binding context is used as an helper to carry out evaluation of certain bindings; for instance,
|
||||
* it helps {@link Allocate} bindings, by providing the {@link SegmentAllocator} that should be used for
|
||||
* the allocation operation, or {@link BoxAddress} bindings, by providing the {@link SegmentScope} that
|
||||
* should be used to create an unsafe struct from a memory address.
|
||||
*/
|
||||
class Context implements AutoCloseable {
|
||||
private final SegmentAllocator allocator;
|
||||
private final SegmentScope scope;
|
||||
|
||||
private Context(SegmentAllocator allocator, SegmentScope scope) {
|
||||
this.allocator = allocator;
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
public SegmentAllocator allocator() {
|
||||
return allocator;
|
||||
}
|
||||
|
||||
public SegmentScope scope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a binding context from given native scope.
|
||||
*/
|
||||
public static Context ofBoundedAllocator(long size) {
|
||||
Arena arena = Arena.openConfined();
|
||||
return new Context(SegmentAllocator.slicingAllocator(MemorySegment.allocateNative(size, arena.scope())), arena.scope()) {
|
||||
@Override
|
||||
public void close() {
|
||||
arena.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a binding context from given segment allocator. The resulting context will throw when
|
||||
* the context's scope is accessed.
|
||||
*/
|
||||
public static Context ofAllocator(SegmentAllocator allocator) {
|
||||
return new Context(allocator, null) {
|
||||
@Override
|
||||
public SegmentScope scope() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a binding context from given scope. The resulting context will throw when
|
||||
* the context's allocator is accessed.
|
||||
*/
|
||||
public static Context ofScope() {
|
||||
Arena arena = Arena.openConfined();
|
||||
return new Context(null, arena.scope()) {
|
||||
@Override
|
||||
public SegmentAllocator allocator() { throw new UnsupportedOperationException(); }
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
arena.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy binding context. Throws exceptions when attempting to access scope, return a throwing allocator, and has
|
||||
* an idempotent {@link #close()}.
|
||||
*/
|
||||
public static final Context DUMMY = new Context(null, null) {
|
||||
@Override
|
||||
public SegmentAllocator allocator() {
|
||||
return SharedUtils.THROWING_ALLOCATOR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SegmentScope scope() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// do nothing
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void verify(Deque<Class<?>> stack);
|
||||
|
||||
void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
|
||||
BindingInterpreter.LoadFunc loadFunc, Context context);
|
||||
void interpret(Deque<Object> stack, StoreFunc storeFunc,
|
||||
LoadFunc loadFunc, SegmentAllocator allocator);
|
||||
|
||||
private static void checkType(Class<?> type) {
|
||||
if (!type.isPrimitive() || type == void.class)
|
||||
@ -352,16 +256,16 @@ public sealed interface Binding {
|
||||
return new Allocate(layout.byteSize(), layout.byteAlignment());
|
||||
}
|
||||
|
||||
static BoxAddress boxAddressRaw(long size) {
|
||||
return new BoxAddress(size, false);
|
||||
static BoxAddress boxAddressRaw(long size, long align) {
|
||||
return new BoxAddress(size, align, false);
|
||||
}
|
||||
|
||||
static BoxAddress boxAddress(MemoryLayout layout) {
|
||||
return new BoxAddress(layout.byteSize(), true);
|
||||
return new BoxAddress(layout.byteSize(), layout.byteAlignment(), true);
|
||||
}
|
||||
|
||||
static BoxAddress boxAddress(long byteSize) {
|
||||
return new BoxAddress(byteSize, true);
|
||||
return new BoxAddress(byteSize, 1, true);
|
||||
}
|
||||
|
||||
static UnboxAddress unboxAddress() {
|
||||
@ -463,8 +367,8 @@ public sealed interface Binding {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Binding.Builder boxAddressRaw(long size) {
|
||||
bindings.add(Binding.boxAddressRaw(size));
|
||||
public Binding.Builder boxAddressRaw(long size, long align) {
|
||||
bindings.add(Binding.boxAddressRaw(size, align));
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -508,8 +412,8 @@ public sealed interface Binding {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
|
||||
BindingInterpreter.LoadFunc loadFunc, Context context) {
|
||||
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
|
||||
LoadFunc loadFunc, SegmentAllocator allocator) {
|
||||
storeFunc.store(storage(), type(), stack.pop());
|
||||
}
|
||||
}
|
||||
@ -527,8 +431,8 @@ public sealed interface Binding {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
|
||||
BindingInterpreter.LoadFunc loadFunc, Context context) {
|
||||
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
|
||||
LoadFunc loadFunc, SegmentAllocator allocator) {
|
||||
stack.push(loadFunc.load(storage(), type()));
|
||||
}
|
||||
}
|
||||
@ -555,8 +459,8 @@ public sealed interface Binding {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
|
||||
BindingInterpreter.LoadFunc loadFunc, Context context) {
|
||||
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
|
||||
LoadFunc loadFunc, SegmentAllocator allocator) {
|
||||
Object value = stack.pop();
|
||||
MemorySegment writeAddress = (MemorySegment) stack.pop();
|
||||
if (SharedUtils.isPowerOfTwo(byteWidth())) {
|
||||
@ -612,8 +516,8 @@ public sealed interface Binding {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
|
||||
BindingInterpreter.LoadFunc loadFunc, Context context) {
|
||||
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
|
||||
LoadFunc loadFunc, SegmentAllocator allocator) {
|
||||
MemorySegment readAddress = (MemorySegment) stack.pop();
|
||||
if (SharedUtils.isPowerOfTwo(byteWidth())) {
|
||||
// exact size match
|
||||
@ -657,8 +561,8 @@ public sealed interface Binding {
|
||||
* and pushes the new buffer onto the operand stack
|
||||
*/
|
||||
record Copy(long size, long alignment) implements Binding {
|
||||
private static MemorySegment copyBuffer(MemorySegment operand, long size, long alignment, Context context) {
|
||||
return context.allocator().allocate(size, alignment)
|
||||
private static MemorySegment copyBuffer(MemorySegment operand, long size, long alignment, SegmentAllocator allocator) {
|
||||
return allocator.allocate(size, alignment)
|
||||
.copyFrom(operand.asSlice(0, size));
|
||||
}
|
||||
|
||||
@ -670,10 +574,10 @@ public sealed interface Binding {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
|
||||
BindingInterpreter.LoadFunc loadFunc, Context context) {
|
||||
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
|
||||
LoadFunc loadFunc, SegmentAllocator allocator) {
|
||||
MemorySegment operand = (MemorySegment) stack.pop();
|
||||
MemorySegment copy = copyBuffer(operand, size, alignment, context);
|
||||
MemorySegment copy = copyBuffer(operand, size, alignment, allocator);
|
||||
stack.push(copy);
|
||||
}
|
||||
}
|
||||
@ -683,8 +587,8 @@ public sealed interface Binding {
|
||||
* Creates a new MemorySegment with the give [size] and [alignment], and pushes it onto the operand stack.
|
||||
*/
|
||||
record Allocate(long size, long alignment) implements Binding {
|
||||
private static MemorySegment allocateBuffer(long size, long alignment, Context context) {
|
||||
return context.allocator().allocate(size, alignment);
|
||||
private static MemorySegment allocateBuffer(long size, long alignment, SegmentAllocator allocator) {
|
||||
return allocator.allocate(size, alignment);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -693,9 +597,9 @@ public sealed interface Binding {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
|
||||
BindingInterpreter.LoadFunc loadFunc, Context context) {
|
||||
stack.push(allocateBuffer(size, alignment, context));
|
||||
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
|
||||
LoadFunc loadFunc, SegmentAllocator allocator) {
|
||||
stack.push(allocateBuffer(size, alignment, allocator));
|
||||
}
|
||||
}
|
||||
|
||||
@ -715,9 +619,9 @@ public sealed interface Binding {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
|
||||
BindingInterpreter.LoadFunc loadFunc, Context context) {
|
||||
stack.push(((MemorySegment)stack.pop()).address());
|
||||
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
|
||||
LoadFunc loadFunc, SegmentAllocator allocator) {
|
||||
stack.push(SharedUtils.unboxSegment((MemorySegment)stack.pop()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -726,7 +630,7 @@ public sealed interface Binding {
|
||||
* Pops a 'long' from the operand stack, converts it to a 'MemorySegment', with the given size and memory scope
|
||||
* (either the context scope, or the global scope), and pushes that onto the operand stack.
|
||||
*/
|
||||
record BoxAddress(long size, boolean needsScope) implements Binding {
|
||||
record BoxAddress(long size, long align, boolean needsScope) implements Binding {
|
||||
|
||||
@Override
|
||||
public void verify(Deque<Class<?>> stack) {
|
||||
@ -736,11 +640,13 @@ public sealed interface Binding {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
|
||||
BindingInterpreter.LoadFunc loadFunc, Context context) {
|
||||
SegmentScope scope = needsScope ?
|
||||
context.scope() : SegmentScope.global();
|
||||
stack.push(NativeMemorySegmentImpl.makeNativeSegmentUnchecked((long) stack.pop(), size, scope));
|
||||
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
|
||||
LoadFunc loadFunc, SegmentAllocator allocator) {
|
||||
MemorySegment segment = Utils.longToAddress((long) stack.pop(), size, align);
|
||||
if (needsScope) {
|
||||
segment = segment.reinterpret((Arena) allocator, null);
|
||||
}
|
||||
stack.push(segment);
|
||||
}
|
||||
}
|
||||
|
||||
@ -758,8 +664,8 @@ public sealed interface Binding {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
|
||||
BindingInterpreter.LoadFunc loadFunc, Context context) {
|
||||
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
|
||||
LoadFunc loadFunc, SegmentAllocator allocator) {
|
||||
stack.push(stack.peekLast());
|
||||
}
|
||||
}
|
||||
@ -773,8 +679,8 @@ public sealed interface Binding {
|
||||
enum Cast implements Binding {
|
||||
INT_TO_BOOLEAN(int.class, boolean.class) {
|
||||
@Override
|
||||
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
|
||||
BindingInterpreter.LoadFunc loadFunc, Context context) {
|
||||
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
|
||||
LoadFunc loadFunc, SegmentAllocator allocator) {
|
||||
// implement least significant byte non-zero test
|
||||
int arg = (int) stack.pop();
|
||||
boolean result = Utils.byteToBoolean((byte) arg);
|
||||
@ -813,8 +719,8 @@ public sealed interface Binding {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interpret(Deque<Object> stack, BindingInterpreter.StoreFunc storeFunc,
|
||||
BindingInterpreter.LoadFunc loadFunc, Context context) {
|
||||
public void interpret(Deque<Object> stack, StoreFunc storeFunc,
|
||||
LoadFunc loadFunc, SegmentAllocator allocator) {
|
||||
Object arg = stack.pop();
|
||||
MethodHandle converter = MethodHandles.explicitCastArguments(MethodHandles.identity(toType),
|
||||
MethodType.methodType(toType, fromType));
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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
|
||||
@ -24,25 +24,26 @@
|
||||
*/
|
||||
package jdk.internal.foreign.abi;
|
||||
|
||||
import java.lang.foreign.SegmentAllocator;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
public class BindingInterpreter {
|
||||
|
||||
static void unbox(Object arg, List<Binding> bindings, StoreFunc storeFunc, Binding.Context context) {
|
||||
static void unbox(Object arg, List<Binding> bindings, StoreFunc storeFunc, SegmentAllocator allocator) {
|
||||
Deque<Object> stack = new ArrayDeque<>();
|
||||
|
||||
stack.push(arg);
|
||||
for (Binding b : bindings) {
|
||||
b.interpret(stack, storeFunc, null, context);
|
||||
b.interpret(stack, storeFunc, null, allocator);
|
||||
}
|
||||
}
|
||||
|
||||
static Object box(List<Binding> bindings, LoadFunc loadFunc, Binding.Context context) {
|
||||
static Object box(List<Binding> bindings, LoadFunc loadFunc, SegmentAllocator allocator) {
|
||||
Deque<Object> stack = new ArrayDeque<>();
|
||||
for (Binding b : bindings) {
|
||||
b.interpret(stack, null, loadFunc, context);
|
||||
b.interpret(stack, null, loadFunc, allocator);
|
||||
}
|
||||
return stack.pop();
|
||||
}
|
||||
|
||||
@ -26,7 +26,6 @@ package jdk.internal.foreign.abi;
|
||||
|
||||
import jdk.internal.foreign.AbstractMemorySegmentImpl;
|
||||
import jdk.internal.foreign.MemorySessionImpl;
|
||||
import jdk.internal.foreign.NativeMemorySegmentImpl;
|
||||
import jdk.internal.foreign.Utils;
|
||||
import jdk.internal.foreign.abi.Binding.Allocate;
|
||||
import jdk.internal.foreign.abi.Binding.BoxAddress;
|
||||
@ -53,12 +52,7 @@ import sun.security.action.GetPropertyAction;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.constant.ConstantDescs;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.SegmentAllocator;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import java.lang.foreign.*;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
@ -88,17 +82,16 @@ public class BindingSpecializer {
|
||||
|
||||
private static final String VOID_DESC = methodType(void.class).descriptorString();
|
||||
|
||||
private static final String BINDING_CONTEXT_DESC = Binding.Context.class.descriptorString();
|
||||
private static final String OF_BOUNDED_ALLOCATOR_DESC = methodType(Binding.Context.class, long.class).descriptorString();
|
||||
private static final String OF_SCOPE_DESC = methodType(Binding.Context.class).descriptorString();
|
||||
private static final String ALLOCATOR_DESC = methodType(SegmentAllocator.class).descriptorString();
|
||||
private static final String SCOPE_DESC = methodType(SegmentScope.class).descriptorString();
|
||||
private static final String ARENA_DESC = Arena.class.descriptorString();
|
||||
private static final String NEW_BOUNDED_ARENA_DESC = methodType(Arena.class, long.class).descriptorString();
|
||||
private static final String NEW_EMPTY_ARENA_DESC = methodType(Arena.class).descriptorString();
|
||||
private static final String SCOPE_DESC = methodType(MemorySegment.Scope.class).descriptorString();
|
||||
private static final String SESSION_IMPL_DESC = methodType(MemorySessionImpl.class).descriptorString();
|
||||
private static final String CLOSE_DESC = VOID_DESC;
|
||||
private static final String UNBOX_SEGMENT_DESC = methodType(long.class, MemorySegment.class).descriptorString();
|
||||
private static final String COPY_DESC = methodType(void.class, MemorySegment.class, long.class, MemorySegment.class, long.class, long.class).descriptorString();
|
||||
private static final String OF_LONG_DESC = methodType(MemorySegment.class, long.class, long.class).descriptorString();
|
||||
private static final String OF_LONG_UNCHECKED_DESC = methodType(MemorySegment.class, long.class, long.class, SegmentScope.class).descriptorString();
|
||||
private static final String LONG_TO_ADDRESS_NO_SCOPE_DESC = methodType(MemorySegment.class, long.class, long.class, long.class).descriptorString();
|
||||
private static final String LONG_TO_ADDRESS_SCOPE_DESC = methodType(MemorySegment.class, long.class, long.class, long.class, MemorySessionImpl.class).descriptorString();
|
||||
private static final String ALLOCATE_DESC = methodType(MemorySegment.class, long.class, long.class).descriptorString();
|
||||
private static final String HANDLE_UNCAUGHT_EXCEPTION_DESC = methodType(void.class, Throwable.class).descriptorString();
|
||||
private static final String METHOD_HANDLES_INTRN = Type.getInternalName(MethodHandles.class);
|
||||
@ -165,7 +158,8 @@ public class BindingSpecializer {
|
||||
byte[] bytes = specializeHelper(leafHandle.type(), callerMethodType, callingSequence, abi);
|
||||
|
||||
try {
|
||||
MethodHandles.Lookup definedClassLookup = MethodHandles.lookup().defineHiddenClassWithClassData(bytes, leafHandle, false);
|
||||
MethodHandles.Lookup definedClassLookup = MethodHandles.lookup()
|
||||
.defineHiddenClassWithClassData(bytes, leafHandle, false);
|
||||
return definedClassLookup.findStatic(definedClassLookup.lookupClass(), METHOD_NAME, callerMethodType);
|
||||
} catch (IllegalAccessException | NoSuchMethodException e) {
|
||||
throw new InternalError("Should not happen", e);
|
||||
@ -294,11 +288,11 @@ public class BindingSpecializer {
|
||||
// create a Binding.Context for this call
|
||||
if (callingSequence.allocationSize() != 0) {
|
||||
emitConst(callingSequence.allocationSize());
|
||||
emitInvokeStatic(Binding.Context.class, "ofBoundedAllocator", OF_BOUNDED_ALLOCATOR_DESC);
|
||||
emitInvokeStatic(SharedUtils.class, "newBoundedArena", NEW_BOUNDED_ARENA_DESC);
|
||||
} else if (callingSequence.forUpcall() && needsSession()) {
|
||||
emitInvokeStatic(Binding.Context.class, "ofScope", OF_SCOPE_DESC);
|
||||
emitInvokeStatic(SharedUtils.class, "newEmptyArena", NEW_EMPTY_ARENA_DESC);
|
||||
} else {
|
||||
emitGetStatic(Binding.Context.class, "DUMMY", BINDING_CONTEXT_DESC);
|
||||
emitGetStatic(SharedUtils.class, "DUMMY_ARENA", ARENA_DESC);
|
||||
}
|
||||
contextIdx = newLocal(Object.class);
|
||||
emitStore(Object.class, contextIdx);
|
||||
@ -422,13 +416,13 @@ public class BindingSpecializer {
|
||||
if (callingSequence.forDowncall()) {
|
||||
mv.visitInsn(ATHROW);
|
||||
} else {
|
||||
emitInvokeStatic(SharedUtils.class, "handleUncaughtException", HANDLE_UNCAUGHT_EXCEPTION_DESC);
|
||||
if (callerMethodType.returnType() != void.class) {
|
||||
emitConstZero(callerMethodType.returnType());
|
||||
emitReturn(callerMethodType.returnType());
|
||||
} else {
|
||||
mv.visitInsn(RETURN);
|
||||
}
|
||||
emitInvokeStatic(SharedUtils.class, "handleUncaughtException", HANDLE_UNCAUGHT_EXCEPTION_DESC);
|
||||
if (callerMethodType.returnType() != void.class) {
|
||||
emitConstZero(callerMethodType.returnType());
|
||||
emitReturn(callerMethodType.returnType());
|
||||
} else {
|
||||
mv.visitInsn(RETURN);
|
||||
}
|
||||
}
|
||||
|
||||
mv.visitTryCatchBlock(tryStart, tryEnd, catchStart, null);
|
||||
@ -454,7 +448,7 @@ public class BindingSpecializer {
|
||||
.get(paramIndex - offset);
|
||||
|
||||
// is this an address layout?
|
||||
return paramLayout instanceof ValueLayout.OfAddress;
|
||||
return paramLayout instanceof AddressLayout;
|
||||
}
|
||||
|
||||
private void emitCleanup() {
|
||||
@ -563,29 +557,32 @@ public class BindingSpecializer {
|
||||
private void emitLoadInternalSession() {
|
||||
assert contextIdx != -1;
|
||||
emitLoad(Object.class, contextIdx);
|
||||
emitInvokeVirtual(Binding.Context.class, "scope", SCOPE_DESC);
|
||||
emitCheckCast(Arena.class);
|
||||
emitInvokeInterface(Arena.class, "scope", SCOPE_DESC);
|
||||
emitCheckCast(MemorySessionImpl.class);
|
||||
}
|
||||
|
||||
private void emitLoadInternalAllocator() {
|
||||
assert contextIdx != -1;
|
||||
emitLoad(Object.class, contextIdx);
|
||||
emitInvokeVirtual(Binding.Context.class, "allocator", ALLOCATOR_DESC);
|
||||
}
|
||||
|
||||
private void emitCloseContext() {
|
||||
assert contextIdx != -1;
|
||||
emitLoad(Object.class, contextIdx);
|
||||
emitInvokeVirtual(Binding.Context.class, "close", CLOSE_DESC);
|
||||
emitCheckCast(Arena.class);
|
||||
emitInvokeInterface(Arena.class, "close", CLOSE_DESC);
|
||||
}
|
||||
|
||||
private void emitBoxAddress(BoxAddress boxAddress) {
|
||||
popType(long.class);
|
||||
emitConst(boxAddress.size());
|
||||
emitConst(boxAddress.align());
|
||||
if (needsSession()) {
|
||||
emitLoadInternalSession();
|
||||
emitInvokeStatic(NativeMemorySegmentImpl.class, "makeNativeSegmentUnchecked", OF_LONG_UNCHECKED_DESC);
|
||||
emitInvokeStatic(Utils.class, "longToAddress", LONG_TO_ADDRESS_SCOPE_DESC);
|
||||
} else {
|
||||
emitInvokeStatic(NativeMemorySegmentImpl.class, "makeNativeSegmentUnchecked", OF_LONG_DESC);
|
||||
emitInvokeStatic(Utils.class, "longToAddress", LONG_TO_ADDRESS_NO_SCOPE_DESC);
|
||||
}
|
||||
pushType(MemorySegment.class);
|
||||
}
|
||||
@ -934,7 +931,7 @@ public class BindingSpecializer {
|
||||
} else if (type == double.class) {
|
||||
return ValueLayout.OfDouble.class;
|
||||
} else if (type == MemorySegment.class) {
|
||||
return ValueLayout.OfAddress.class;
|
||||
return AddressLayout.class;
|
||||
} else {
|
||||
throw new IllegalStateException("Unknown type: " + type);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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
|
||||
@ -191,6 +191,10 @@ public class CallingSequence {
|
||||
.reduce(0, (a, b) -> a | b);
|
||||
}
|
||||
|
||||
public boolean needsTransition() {
|
||||
return !linkerOptions.isTrivial();
|
||||
}
|
||||
|
||||
public int numLeadingParams() {
|
||||
return 2 + (linkerOptions.hasCapturedCallState() ? 1 : 0); // 2 for addr, allocator
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -24,34 +24,49 @@
|
||||
*/
|
||||
package jdk.internal.foreign.abi;
|
||||
|
||||
import jdk.internal.foreign.Utils;
|
||||
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.StructLayout;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.lang.foreign.ValueLayout.JAVA_INT;
|
||||
import static sun.security.action.GetPropertyAction.privilegedGetProperty;
|
||||
|
||||
public enum CapturableState {
|
||||
GET_LAST_ERROR ("GetLastError", JAVA_INT, 1 << 0),
|
||||
WSA_GET_LAST_ERROR("WSAGetLastError", JAVA_INT, 1 << 1),
|
||||
ERRNO ("errno", JAVA_INT, 1 << 2);
|
||||
GET_LAST_ERROR ("GetLastError", JAVA_INT, 1 << 0, Utils.IS_WINDOWS),
|
||||
WSA_GET_LAST_ERROR("WSAGetLastError", JAVA_INT, 1 << 1, Utils.IS_WINDOWS),
|
||||
ERRNO ("errno", JAVA_INT, 1 << 2, true);
|
||||
|
||||
public static final StructLayout LAYOUT = MemoryLayout.structLayout(
|
||||
supportedStates().map(CapturableState::layout).toArray(MemoryLayout[]::new));
|
||||
|
||||
private final String stateName;
|
||||
private final ValueLayout layout;
|
||||
private final int mask;
|
||||
private final boolean isSupported;
|
||||
|
||||
CapturableState(String stateName, ValueLayout layout, int mask) {
|
||||
CapturableState(String stateName, ValueLayout layout, int mask, boolean isSupported) {
|
||||
this.stateName = stateName;
|
||||
this.layout = layout.withName(stateName);
|
||||
this.mask = mask;
|
||||
this.isSupported = isSupported;
|
||||
}
|
||||
|
||||
private static Stream<CapturableState> supportedStates() {
|
||||
return Stream.of(values()).filter(CapturableState::isSupported);
|
||||
}
|
||||
|
||||
public static CapturableState forName(String name) {
|
||||
return Stream.of(values())
|
||||
.filter(stl -> stl.stateName().equals(name))
|
||||
.filter(CapturableState::isSupported)
|
||||
.findAny()
|
||||
.orElseThrow(() -> new IllegalArgumentException(
|
||||
"Unknown name: " + name +", must be one of: "
|
||||
+ Stream.of(CapturableState.values())
|
||||
+ supportedStates()
|
||||
.map(CapturableState::stateName)
|
||||
.collect(Collectors.joining(", "))));
|
||||
}
|
||||
@ -67,4 +82,8 @@ public enum CapturableState {
|
||||
public int mask() {
|
||||
return mask;
|
||||
}
|
||||
|
||||
public boolean isSupported() {
|
||||
return isSupported;
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,6 +28,7 @@ import jdk.internal.access.JavaLangInvokeAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentAllocator;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
@ -50,7 +51,6 @@ public class DowncallLinker {
|
||||
private static final JavaLangInvokeAccess JLIA = SharedSecrets.getJavaLangInvokeAccess();
|
||||
|
||||
private static final MethodHandle MH_INVOKE_INTERP_BINDINGS;
|
||||
private static final MethodHandle MH_CHECK_SYMBOL;
|
||||
private static final MethodHandle EMPTY_OBJECT_ARRAY_HANDLE = MethodHandles.constant(Object[].class, new Object[0]);
|
||||
|
||||
static {
|
||||
@ -58,8 +58,6 @@ public class DowncallLinker {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
MH_INVOKE_INTERP_BINDINGS = lookup.findVirtual(DowncallLinker.class, "invokeInterpBindings",
|
||||
methodType(Object.class, SegmentAllocator.class, Object[].class, InvocationData.class));
|
||||
MH_CHECK_SYMBOL = lookup.findStatic(SharedUtils.class, "checkSymbol",
|
||||
methodType(void.class, MemorySegment.class));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@ -86,7 +84,8 @@ public class DowncallLinker {
|
||||
toStorageArray(retMoves),
|
||||
leafType,
|
||||
callingSequence.needsReturnBuffer(),
|
||||
callingSequence.capturedStateMask()
|
||||
callingSequence.capturedStateMask(),
|
||||
callingSequence.needsTransition()
|
||||
);
|
||||
MethodHandle handle = JLIA.nativeMethodHandle(nep);
|
||||
|
||||
@ -111,7 +110,7 @@ public class DowncallLinker {
|
||||
|
||||
assert handle.type().parameterType(0) == SegmentAllocator.class;
|
||||
assert handle.type().parameterType(1) == MemorySegment.class;
|
||||
handle = foldArguments(handle, 1, MH_CHECK_SYMBOL);
|
||||
handle = foldArguments(handle, 1, SharedUtils.MH_CHECK_SYMBOL);
|
||||
|
||||
handle = SharedUtils.swapArguments(handle, 0, 1); // normalize parameter order
|
||||
|
||||
@ -150,10 +149,10 @@ public class DowncallLinker {
|
||||
private record InvocationData(MethodHandle leaf, Map<VMStorage, Integer> argIndexMap, Map<VMStorage, Integer> retIndexMap) {}
|
||||
|
||||
Object invokeInterpBindings(SegmentAllocator allocator, Object[] args, InvocationData invData) throws Throwable {
|
||||
Binding.Context unboxContext = callingSequence.allocationSize() != 0
|
||||
? Binding.Context.ofBoundedAllocator(callingSequence.allocationSize())
|
||||
: Binding.Context.DUMMY;
|
||||
try (unboxContext) {
|
||||
Arena unboxArena = callingSequence.allocationSize() != 0
|
||||
? SharedUtils.newBoundedArena(callingSequence.allocationSize())
|
||||
: SharedUtils.DUMMY_ARENA;
|
||||
try (unboxArena) {
|
||||
MemorySegment returnBuffer = null;
|
||||
|
||||
// do argument processing, get Object[] as result
|
||||
@ -161,7 +160,7 @@ public class DowncallLinker {
|
||||
if (callingSequence.needsReturnBuffer()) {
|
||||
// we supply the return buffer (argument array does not contain it)
|
||||
Object[] prefixedArgs = new Object[args.length + 1];
|
||||
returnBuffer = unboxContext.allocator().allocate(callingSequence.returnBufferSize());
|
||||
returnBuffer = unboxArena.allocate(callingSequence.returnBufferSize());
|
||||
prefixedArgs[0] = returnBuffer;
|
||||
System.arraycopy(args, 0, prefixedArgs, 1, args.length);
|
||||
args = prefixedArgs;
|
||||
@ -169,7 +168,7 @@ public class DowncallLinker {
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
Object arg = args[i];
|
||||
BindingInterpreter.unbox(arg, callingSequence.argumentBindings(i),
|
||||
(storage, type, value) -> leafArgs[invData.argIndexMap.get(storage)] = value, unboxContext);
|
||||
(storage, type, value) -> leafArgs[invData.argIndexMap.get(storage)] = value, unboxArena);
|
||||
}
|
||||
|
||||
// call leaf
|
||||
@ -190,10 +189,10 @@ public class DowncallLinker {
|
||||
retBufReadOffset += abi.arch.typeSize(storage.type());
|
||||
return result1;
|
||||
}
|
||||
}, Binding.Context.ofAllocator(allocator));
|
||||
}, allocator);
|
||||
} else {
|
||||
return BindingInterpreter.box(callingSequence.returnBindings(), (storage, type) -> o,
|
||||
Binding.Context.ofAllocator(allocator));
|
||||
allocator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,13 +26,11 @@ package jdk.internal.foreign.abi;
|
||||
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.Linker;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.StructLayout;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class LinkerOptions {
|
||||
@ -45,14 +43,23 @@ public class LinkerOptions {
|
||||
}
|
||||
|
||||
public static LinkerOptions forDowncall(FunctionDescriptor desc, Linker.Option... options) {
|
||||
Map<Class<?>, LinkerOptionImpl> optionMap = new HashMap<>();
|
||||
return forShared(LinkerOptionImpl::validateForDowncall, desc, options);
|
||||
}
|
||||
|
||||
public static LinkerOptions forUpcall(FunctionDescriptor desc, Linker.Option[] options) {
|
||||
return forShared(LinkerOptionImpl::validateForUpcall, desc, options);
|
||||
}
|
||||
|
||||
private static LinkerOptions forShared(BiConsumer<LinkerOptionImpl, FunctionDescriptor> validator,
|
||||
FunctionDescriptor desc, Linker.Option... options) {
|
||||
Map<Class<?>, LinkerOptionImpl> optionMap = new HashMap<>();
|
||||
|
||||
for (Linker.Option option : options) {
|
||||
if (optionMap.containsKey(option.getClass())) {
|
||||
throw new IllegalArgumentException("Duplicate option: " + option);
|
||||
}
|
||||
LinkerOptionImpl opImpl = (LinkerOptionImpl) option;
|
||||
opImpl.validateForDowncall(desc);
|
||||
validator.accept(opImpl, desc);
|
||||
optionMap.put(option.getClass(), opImpl);
|
||||
}
|
||||
|
||||
@ -73,11 +80,11 @@ public class LinkerOptions {
|
||||
}
|
||||
|
||||
public boolean hasCapturedCallState() {
|
||||
return getOption(CaptureCallStateImpl.class) != null;
|
||||
return getOption(CaptureCallState.class) != null;
|
||||
}
|
||||
|
||||
public Stream<CapturableState> capturedCallState() {
|
||||
CaptureCallStateImpl stl = getOption(CaptureCallStateImpl.class);
|
||||
CaptureCallState stl = getOption(CaptureCallState.class);
|
||||
return stl == null ? Stream.empty() : stl.saved().stream();
|
||||
}
|
||||
|
||||
@ -86,6 +93,11 @@ public class LinkerOptions {
|
||||
return fva != null;
|
||||
}
|
||||
|
||||
public boolean isTrivial() {
|
||||
IsTrivial it = getOption(IsTrivial.class);
|
||||
return it != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
@ -99,11 +111,14 @@ public class LinkerOptions {
|
||||
}
|
||||
|
||||
public sealed interface LinkerOptionImpl extends Linker.Option
|
||||
permits FirstVariadicArg,
|
||||
CaptureCallStateImpl {
|
||||
permits CaptureCallState, FirstVariadicArg, IsTrivial {
|
||||
default void validateForDowncall(FunctionDescriptor descriptor) {
|
||||
throw new IllegalArgumentException("Not supported for downcall: " + this);
|
||||
}
|
||||
|
||||
default void validateForUpcall(FunctionDescriptor descriptor) {
|
||||
throw new IllegalArgumentException("Not supported for upcall: " + this);
|
||||
}
|
||||
}
|
||||
|
||||
public record FirstVariadicArg(int index) implements LinkerOptionImpl {
|
||||
@ -115,22 +130,19 @@ public class LinkerOptions {
|
||||
}
|
||||
}
|
||||
|
||||
public record CaptureCallStateImpl(Set<CapturableState> saved) implements LinkerOptionImpl, Linker.Option.CaptureCallState {
|
||||
|
||||
public record CaptureCallState(Set<CapturableState> saved) implements LinkerOptionImpl {
|
||||
@Override
|
||||
public void validateForDowncall(FunctionDescriptor descriptor) {
|
||||
// done during construction
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructLayout layout() {
|
||||
return MemoryLayout.structLayout(
|
||||
saved.stream()
|
||||
.sorted(Comparator.comparingInt(CapturableState::ordinal))
|
||||
.map(CapturableState::layout)
|
||||
.toArray(MemoryLayout[]::new)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public record IsTrivial() implements LinkerOptionImpl {
|
||||
public static IsTrivial INSTANCE = new IsTrivial();
|
||||
|
||||
@Override
|
||||
public void validateForDowncall(FunctionDescriptor descriptor) {
|
||||
// always allowed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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
|
||||
@ -47,7 +47,8 @@ public class NativeEntryPoint {
|
||||
private static final SoftReferenceCache<CacheKey, NativeEntryPoint> NEP_CACHE = new SoftReferenceCache<>();
|
||||
private record CacheKey(MethodType methodType, ABIDescriptor abi,
|
||||
List<VMStorage> argMoves, List<VMStorage> retMoves,
|
||||
boolean needsReturnBuffer, int capturedStateMask) {}
|
||||
boolean needsReturnBuffer, int capturedStateMask,
|
||||
boolean needsTransition) {}
|
||||
|
||||
private NativeEntryPoint(MethodType methodType, long downcallStubAddress) {
|
||||
this.methodType = methodType;
|
||||
@ -58,15 +59,18 @@ public class NativeEntryPoint {
|
||||
VMStorage[] argMoves, VMStorage[] returnMoves,
|
||||
MethodType methodType,
|
||||
boolean needsReturnBuffer,
|
||||
int capturedStateMask) {
|
||||
int capturedStateMask,
|
||||
boolean needsTransition) {
|
||||
if (returnMoves.length > 1 != needsReturnBuffer) {
|
||||
throw new AssertionError("Multiple register return, but needsReturnBuffer was false");
|
||||
}
|
||||
checkType(methodType, needsReturnBuffer, capturedStateMask);
|
||||
|
||||
CacheKey key = new CacheKey(methodType, abi, Arrays.asList(argMoves), Arrays.asList(returnMoves), needsReturnBuffer, capturedStateMask);
|
||||
CacheKey key = new CacheKey(methodType, abi, Arrays.asList(argMoves), Arrays.asList(returnMoves),
|
||||
needsReturnBuffer, capturedStateMask, needsTransition);
|
||||
return NEP_CACHE.get(key, k -> {
|
||||
long downcallStub = makeDowncallStub(methodType, abi, argMoves, returnMoves, needsReturnBuffer, capturedStateMask);
|
||||
long downcallStub = makeDowncallStub(methodType, abi, argMoves, returnMoves, needsReturnBuffer,
|
||||
capturedStateMask, needsTransition);
|
||||
NativeEntryPoint nep = new NativeEntryPoint(methodType, downcallStub);
|
||||
CLEANER.register(nep, () -> freeDowncallStub(downcallStub));
|
||||
return nep;
|
||||
@ -87,7 +91,8 @@ public class NativeEntryPoint {
|
||||
private static native long makeDowncallStub(MethodType methodType, ABIDescriptor abi,
|
||||
VMStorage[] encArgMoves, VMStorage[] encRetMoves,
|
||||
boolean needsReturnBuffer,
|
||||
int capturedStateMask);
|
||||
int capturedStateMask,
|
||||
boolean needsTransition);
|
||||
|
||||
private static native boolean freeDowncallStub0(long downcallStub);
|
||||
private static void freeDowncallStub(long downcallStub) {
|
||||
|
||||
@ -32,19 +32,21 @@ import jdk.internal.foreign.abi.AbstractLinker.UpcallStubFactory;
|
||||
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.riscv64.linux.LinuxRISCV64Linker;
|
||||
import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
|
||||
import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
|
||||
import java.lang.foreign.AddressLayout;
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.Linker;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.MemorySegment.Scope;
|
||||
import java.lang.foreign.SegmentAllocator;
|
||||
import java.lang.foreign.VaList;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
@ -55,9 +57,7 @@ import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@ -76,6 +76,28 @@ public final class SharedUtils {
|
||||
private static final MethodHandle MH_ALLOC_BUFFER;
|
||||
private static final MethodHandle MH_BUFFER_COPY;
|
||||
private static final MethodHandle MH_REACHABILITY_FENCE;
|
||||
public static final MethodHandle MH_CHECK_SYMBOL;
|
||||
|
||||
public static final AddressLayout C_POINTER = ADDRESS
|
||||
.withBitAlignment(64)
|
||||
.withTargetLayout(MemoryLayout.sequenceLayout(JAVA_BYTE));
|
||||
|
||||
public static final Arena DUMMY_ARENA = new Arena() {
|
||||
@Override
|
||||
public Scope scope() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment allocate(long byteSize) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// do nothing
|
||||
}
|
||||
};
|
||||
|
||||
static {
|
||||
try {
|
||||
@ -86,6 +108,8 @@ public final class SharedUtils {
|
||||
methodType(MemorySegment.class, MemorySegment.class, MemorySegment.class));
|
||||
MH_REACHABILITY_FENCE = lookup.findStatic(Reference.class, "reachabilityFence",
|
||||
methodType(void.class, Object.class));
|
||||
MH_CHECK_SYMBOL = lookup.findStatic(SharedUtils.class, "checkSymbol",
|
||||
methodType(void.class, MemorySegment.class));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new BootstrapMethodError(e);
|
||||
}
|
||||
@ -214,6 +238,8 @@ public final class SharedUtils {
|
||||
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.getInstance();
|
||||
case WIN_AARCH_64 -> WindowsAArch64Linker.getInstance();
|
||||
case LINUX_RISCV_64 -> LinuxRISCV64Linker.getInstance();
|
||||
case FALLBACK -> FallbackLinker.getInstance();
|
||||
case UNSUPPORTED -> throw new UnsupportedOperationException("Platform does not support native linker");
|
||||
};
|
||||
}
|
||||
|
||||
@ -265,7 +291,7 @@ public final class SharedUtils {
|
||||
}
|
||||
|
||||
|
||||
static MethodHandle swapArguments(MethodHandle mh, int firstArg, int secondArg) {
|
||||
public static MethodHandle swapArguments(MethodHandle mh, int firstArg, int secondArg) {
|
||||
MethodType mtype = mh.type();
|
||||
int[] perms = new int[mtype.parameterCount()];
|
||||
MethodType swappedType = MethodType.methodType(mtype.returnType());
|
||||
@ -283,10 +309,14 @@ public final class SharedUtils {
|
||||
return MH_REACHABILITY_FENCE.asType(MethodType.methodType(void.class, type));
|
||||
}
|
||||
|
||||
static void handleUncaughtException(Throwable t) {
|
||||
public static void handleUncaughtException(Throwable t) {
|
||||
if (t != null) {
|
||||
t.printStackTrace();
|
||||
JLA.exit(1);
|
||||
try {
|
||||
t.printStackTrace();
|
||||
System.err.println("Unrecoverable uncaught exception encountered. The VM will now exit");
|
||||
} finally {
|
||||
JLA.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -319,39 +349,6 @@ public final class SharedUtils {
|
||||
throw new IllegalArgumentException("Symbol is NULL: " + symbol);
|
||||
}
|
||||
|
||||
public static VaList newVaList(Consumer<VaList.Builder> actions, SegmentScope scope) {
|
||||
return switch (CABI.current()) {
|
||||
case WIN_64 -> Windowsx64Linker.newVaList(actions, scope);
|
||||
case SYS_V -> SysVx64Linker.newVaList(actions, scope);
|
||||
case LINUX_AARCH_64 -> LinuxAArch64Linker.newVaList(actions, scope);
|
||||
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.newVaList(actions, scope);
|
||||
case LINUX_RISCV_64 -> LinuxRISCV64Linker.newVaList(actions, scope);
|
||||
case WIN_AARCH_64 -> WindowsAArch64Linker.newVaList(actions, scope);
|
||||
};
|
||||
}
|
||||
|
||||
public static VaList newVaListOfAddress(long address, SegmentScope scope) {
|
||||
return switch (CABI.current()) {
|
||||
case WIN_64 -> Windowsx64Linker.newVaListOfAddress(address, scope);
|
||||
case SYS_V -> SysVx64Linker.newVaListOfAddress(address, scope);
|
||||
case LINUX_AARCH_64 -> LinuxAArch64Linker.newVaListOfAddress(address, scope);
|
||||
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.newVaListOfAddress(address, scope);
|
||||
case LINUX_RISCV_64 -> LinuxRISCV64Linker.newVaListOfAddress(address, scope);
|
||||
case WIN_AARCH_64 -> WindowsAArch64Linker.newVaListOfAddress(address, scope);
|
||||
};
|
||||
}
|
||||
|
||||
public static VaList emptyVaList() {
|
||||
return switch (CABI.current()) {
|
||||
case WIN_64 -> Windowsx64Linker.emptyVaList();
|
||||
case SYS_V -> SysVx64Linker.emptyVaList();
|
||||
case LINUX_AARCH_64 -> LinuxAArch64Linker.emptyVaList();
|
||||
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.emptyVaList();
|
||||
case LINUX_RISCV_64 -> LinuxRISCV64Linker.emptyVaList();
|
||||
case WIN_AARCH_64 -> WindowsAArch64Linker.emptyVaList();
|
||||
};
|
||||
}
|
||||
|
||||
static void checkType(Class<?> actualType, Class<?> expectedType) {
|
||||
if (expectedType != actualType) {
|
||||
throw new IllegalArgumentException(
|
||||
@ -369,8 +366,47 @@ public final class SharedUtils {
|
||||
: chunkOffset;
|
||||
}
|
||||
|
||||
public static NoSuchElementException newVaListNSEE(MemoryLayout layout) {
|
||||
return new NoSuchElementException("No such element: " + layout);
|
||||
public static Arena newBoundedArena(long size) {
|
||||
return new Arena() {
|
||||
final Arena arena = Arena.ofConfined();
|
||||
final SegmentAllocator slicingAllocator = SegmentAllocator.slicingAllocator(arena.allocate(size));
|
||||
|
||||
@Override
|
||||
public Scope scope() {
|
||||
return arena.scope();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
arena.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment allocate(long byteSize, long byteAlignment) {
|
||||
return slicingAllocator.allocate(byteSize, byteAlignment);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Arena newEmptyArena() {
|
||||
return new Arena() {
|
||||
final Arena arena = Arena.ofConfined();
|
||||
|
||||
@Override
|
||||
public Scope scope() {
|
||||
return arena.scope();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
arena.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment allocate(long byteSize, long byteAlignment) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static final class SimpleVaArg {
|
||||
@ -387,59 +423,6 @@ public final class SharedUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class EmptyVaList implements VaList {
|
||||
|
||||
private final MemorySegment address;
|
||||
|
||||
public EmptyVaList(MemorySegment address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
private static UnsupportedOperationException uoe() {
|
||||
return new UnsupportedOperationException("Empty VaList");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextVarg(ValueLayout.OfInt layout) {
|
||||
throw uoe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextVarg(ValueLayout.OfLong layout) {
|
||||
throw uoe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextVarg(ValueLayout.OfDouble layout) {
|
||||
throw uoe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(ValueLayout.OfAddress layout) {
|
||||
throw uoe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
|
||||
throw uoe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(MemoryLayout... layouts) {
|
||||
throw uoe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaList copy() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment segment() {
|
||||
return address;
|
||||
}
|
||||
}
|
||||
|
||||
static void writeOverSized(MemorySegment ptr, Class<?> type, Object o) {
|
||||
// use VH_LONG for integers to zero out the whole register in the process
|
||||
if (type == long.class) {
|
||||
|
||||
@ -28,8 +28,8 @@ package jdk.internal.foreign.abi;
|
||||
import jdk.internal.foreign.abi.AbstractLinker.UpcallStubFactory;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
@ -141,9 +141,9 @@ public class UpcallLinker {
|
||||
ABIDescriptor abi) {}
|
||||
|
||||
private static Object invokeInterpBindings(MethodHandle leaf, Object[] lowLevelArgs, InvocationData invData) throws Throwable {
|
||||
Binding.Context allocator = invData.callingSequence.allocationSize() != 0
|
||||
? Binding.Context.ofBoundedAllocator(invData.callingSequence.allocationSize())
|
||||
: Binding.Context.ofScope();
|
||||
Arena allocator = invData.callingSequence.allocationSize() != 0
|
||||
? SharedUtils.newBoundedArena(invData.callingSequence.allocationSize())
|
||||
: SharedUtils.newEmptyArena();
|
||||
try (allocator) {
|
||||
/// Invoke interpreter, got array of high-level arguments back
|
||||
Object[] highLevelArgs = new Object[invData.callingSequence.calleeMethodType().parameterCount()];
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 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,7 +25,7 @@
|
||||
package jdk.internal.foreign.abi;
|
||||
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.Arena;
|
||||
|
||||
import jdk.internal.foreign.MemorySessionImpl;
|
||||
|
||||
@ -50,13 +50,13 @@ public final class UpcallStubs {
|
||||
registerNatives();
|
||||
}
|
||||
|
||||
static MemorySegment makeUpcall(long entry, SegmentScope scope) {
|
||||
((MemorySessionImpl) scope).addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() {
|
||||
static MemorySegment makeUpcall(long entry, Arena arena) {
|
||||
MemorySessionImpl.toMemorySession(arena).addOrCleanupIfFail(new MemorySessionImpl.ResourceList.ResourceCleanup() {
|
||||
@Override
|
||||
public void cleanup() {
|
||||
freeUpcallStub(entry);
|
||||
}
|
||||
});
|
||||
return MemorySegment.ofAddress(entry, 0, scope);
|
||||
return MemorySegment.ofAddress(entry).reinterpret(arena, null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ import jdk.internal.foreign.abi.Architecture;
|
||||
import jdk.internal.foreign.abi.StubLocations;
|
||||
import jdk.internal.foreign.abi.VMStorage;
|
||||
|
||||
public class AArch64Architecture implements Architecture {
|
||||
public final class AArch64Architecture implements Architecture {
|
||||
public static final Architecture INSTANCE = new AArch64Architecture();
|
||||
|
||||
private static final short REG64_MASK = 0b0000_0000_0000_0001;
|
||||
@ -39,6 +39,9 @@ public class AArch64Architecture implements Architecture {
|
||||
private static final int INTEGER_REG_SIZE = 8;
|
||||
private static final int VECTOR_REG_SIZE = 16;
|
||||
|
||||
// Suppresses default constructor, ensuring non-instantiability.
|
||||
private AArch64Architecture() {}
|
||||
|
||||
@Override
|
||||
public boolean isStackType(int cls) {
|
||||
return cls == StorageType.STACK;
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
*/
|
||||
package jdk.internal.foreign.abi.aarch64;
|
||||
|
||||
import java.lang.foreign.AddressLayout;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
@ -44,14 +45,12 @@ import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64CallArranger;
|
||||
import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64CallArranger;
|
||||
import jdk.internal.foreign.Utils;
|
||||
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static jdk.internal.foreign.PlatformLayouts.*;
|
||||
import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.*;
|
||||
import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.Regs.*;
|
||||
|
||||
@ -82,7 +81,7 @@ public abstract class CallArranger {
|
||||
//
|
||||
// Although the AAPCS64 says r0-7 and v0-7 are all valid return
|
||||
// registers, it's not possible to generate a C function that uses
|
||||
// r2-7 and v4-7 so they are omitted here.
|
||||
// r2-7 and v4-7 so, they are omitted here.
|
||||
protected static final ABIDescriptor C = abiFor(
|
||||
new VMStorage[] { r0, r1, r2, r3, r4, r5, r6, r7, INDIRECT_RESULT},
|
||||
new VMStorage[] { v0, v1, v2, v3, v4, v5, v6, v7 },
|
||||
@ -158,7 +157,7 @@ public abstract class CallArranger {
|
||||
|
||||
boolean returnInMemory = isInMemoryReturn(cDesc.returnLayout());
|
||||
if (returnInMemory) {
|
||||
csb.addArgumentBindings(MemorySegment.class, AArch64.C_POINTER,
|
||||
csb.addArgumentBindings(MemorySegment.class, SharedUtils.C_POINTER,
|
||||
argCalc.getIndirectBindings());
|
||||
} else if (cDesc.returnLayout().isPresent()) {
|
||||
Class<?> carrier = mt.returnType();
|
||||
@ -190,8 +189,9 @@ public abstract class CallArranger {
|
||||
return handle;
|
||||
}
|
||||
|
||||
public UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc) {
|
||||
Bindings bindings = getBindings(mt, cDesc, true);
|
||||
public UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc,
|
||||
LinkerOptions options) {
|
||||
Bindings bindings = getBindings(mt, cDesc, true, options);
|
||||
final boolean dropReturn = true; /* drop return, since we don't have bindings for it */
|
||||
return SharedUtils.arrangeUpcallHelper(mt, bindings.isInMemoryReturn, dropReturn, abiDescriptor(),
|
||||
bindings.callingSequence);
|
||||
@ -429,7 +429,7 @@ public abstract class CallArranger {
|
||||
assert carrier == MemorySegment.class;
|
||||
bindings.copy(layout)
|
||||
.unboxAddress();
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, AArch64.C_POINTER);
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, SharedUtils.C_POINTER);
|
||||
bindings.vmStore(storage, long.class);
|
||||
}
|
||||
case POINTER -> {
|
||||
@ -464,7 +464,7 @@ public abstract class CallArranger {
|
||||
List<Binding> getIndirectBindings() {
|
||||
return Binding.builder()
|
||||
.vmLoad(INDIRECT_RESULT, long.class)
|
||||
.boxAddressRaw(Long.MAX_VALUE)
|
||||
.boxAddressRaw(Long.MAX_VALUE, 1)
|
||||
.build();
|
||||
}
|
||||
|
||||
@ -480,22 +480,25 @@ public abstract class CallArranger {
|
||||
StorageCalculator.StructStorage[] structStorages
|
||||
= storageCalculator.structStorages((GroupLayout) layout, forHFA);
|
||||
|
||||
for (StorageCalculator.StructStorage structStorage : structStorages) {
|
||||
for (StorageCalculator.StructStorage(
|
||||
long offset, Class<?> ca, int byteWidth, VMStorage storage
|
||||
) : structStorages) {
|
||||
bindings.dup();
|
||||
bindings.vmLoad(structStorage.storage(), structStorage.carrier())
|
||||
.bufferStore(structStorage.offset(), structStorage.carrier(), structStorage.byteWidth());
|
||||
bindings.vmLoad(storage, ca)
|
||||
.bufferStore(offset, ca, byteWidth);
|
||||
}
|
||||
}
|
||||
case STRUCT_REFERENCE -> {
|
||||
assert carrier == MemorySegment.class;
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, AArch64.C_POINTER);
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, SharedUtils.C_POINTER);
|
||||
bindings.vmLoad(storage, long.class)
|
||||
.boxAddress(layout);
|
||||
}
|
||||
case POINTER -> {
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, (ValueLayout) layout);
|
||||
AddressLayout addressLayout = (AddressLayout) layout;
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, addressLayout);
|
||||
bindings.vmLoad(storage, long.class)
|
||||
.boxAddressRaw(Utils.pointeeSize(layout));
|
||||
.boxAddressRaw(Utils.pointeeByteSize(addressLayout), Utils.pointeeByteAlign(addressLayout));
|
||||
}
|
||||
case INTEGER -> {
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, (ValueLayout) layout);
|
||||
|
||||
@ -29,12 +29,9 @@ import jdk.internal.foreign.abi.AbstractLinker;
|
||||
import jdk.internal.foreign.abi.LinkerOptions;
|
||||
import jdk.internal.foreign.abi.aarch64.CallArranger;
|
||||
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.VaList;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* ABI implementation based on ARM document "Procedure Call Standard for
|
||||
@ -60,21 +57,7 @@ public final class LinuxAArch64Linker extends AbstractLinker {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function) {
|
||||
return CallArranger.LINUX.arrangeUpcall(targetType, function);
|
||||
}
|
||||
|
||||
public static VaList newVaList(Consumer<VaList.Builder> actions, SegmentScope scope) {
|
||||
LinuxAArch64VaList.Builder builder = LinuxAArch64VaList.builder(scope);
|
||||
actions.accept(builder);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static VaList newVaListOfAddress(long address, SegmentScope scope) {
|
||||
return LinuxAArch64VaList.ofAddress(address, scope);
|
||||
}
|
||||
|
||||
public static VaList emptyVaList() {
|
||||
return LinuxAArch64VaList.empty();
|
||||
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) {
|
||||
return CallArranger.LINUX.arrangeUpcall(targetType, function, options);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,568 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2021, Arm Limited. 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.aarch64.linux;
|
||||
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import java.lang.foreign.VaList;
|
||||
import java.lang.foreign.SegmentAllocator;
|
||||
import jdk.internal.foreign.abi.aarch64.TypeClass;
|
||||
import jdk.internal.foreign.MemorySessionImpl;
|
||||
import jdk.internal.foreign.Utils;
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static jdk.internal.foreign.PlatformLayouts.AArch64;
|
||||
|
||||
import static java.lang.foreign.MemoryLayout.PathElement.groupElement;
|
||||
import static jdk.internal.foreign.abi.SharedUtils.SimpleVaArg;
|
||||
import static jdk.internal.foreign.abi.SharedUtils.THROWING_ALLOCATOR;
|
||||
import static jdk.internal.foreign.abi.aarch64.CallArranger.MAX_REGISTER_ARGUMENTS;
|
||||
|
||||
/**
|
||||
* Standard va_list implementation as defined by AAPCS document and used on
|
||||
* Linux. Variadic parameters may be passed in registers or on the stack.
|
||||
*/
|
||||
public non-sealed class LinuxAArch64VaList implements VaList {
|
||||
|
||||
// See AAPCS Appendix B "Variable Argument Lists" for definition of
|
||||
// va_list on AArch64.
|
||||
//
|
||||
// typedef struct __va_list {
|
||||
// void *__stack; // next stack param
|
||||
// void *__gr_top; // end of GP arg reg save area
|
||||
// void *__vr_top; // end of FP/SIMD arg reg save area
|
||||
// int __gr_offs; // offset from __gr_top to next GP register arg
|
||||
// int __vr_offs; // offset from __vr_top to next FP/SIMD register arg
|
||||
// } va_list;
|
||||
|
||||
static final GroupLayout LAYOUT = MemoryLayout.structLayout(
|
||||
AArch64.C_POINTER.withName("__stack"),
|
||||
AArch64.C_POINTER.withName("__gr_top"),
|
||||
AArch64.C_POINTER.withName("__vr_top"),
|
||||
AArch64.C_INT.withName("__gr_offs"),
|
||||
AArch64.C_INT.withName("__vr_offs")
|
||||
).withName("__va_list");
|
||||
|
||||
private static final long STACK_SLOT_SIZE = 8;
|
||||
|
||||
private static final MemoryLayout GP_REG
|
||||
= MemoryLayout.paddingLayout(64).withBitAlignment(64);
|
||||
private static final MemoryLayout FP_REG
|
||||
= MemoryLayout.paddingLayout(128).withBitAlignment(128);
|
||||
|
||||
private static final MemoryLayout LAYOUT_GP_REGS
|
||||
= MemoryLayout.sequenceLayout(MAX_REGISTER_ARGUMENTS, GP_REG);
|
||||
private static final MemoryLayout LAYOUT_FP_REGS
|
||||
= MemoryLayout.sequenceLayout(MAX_REGISTER_ARGUMENTS, FP_REG);
|
||||
|
||||
private static final int GP_SLOT_SIZE = (int) GP_REG.byteSize();
|
||||
private static final int FP_SLOT_SIZE = (int) FP_REG.byteSize();
|
||||
|
||||
private static final int MAX_GP_OFFSET = (int) LAYOUT_GP_REGS.byteSize();
|
||||
private static final int MAX_FP_OFFSET = (int) LAYOUT_FP_REGS.byteSize();
|
||||
|
||||
private static final VarHandle VH_stack = LAYOUT.varHandle(groupElement("__stack"));
|
||||
private static final VarHandle VH_gr_top = LAYOUT.varHandle(groupElement("__gr_top"));
|
||||
private static final VarHandle VH_vr_top = LAYOUT.varHandle(groupElement("__vr_top"));
|
||||
private static final VarHandle VH_gr_offs
|
||||
= LAYOUT.varHandle(groupElement("__gr_offs"));
|
||||
private static final VarHandle VH_vr_offs
|
||||
= LAYOUT.varHandle(groupElement("__vr_offs"));
|
||||
|
||||
private static final VaList EMPTY
|
||||
= new SharedUtils.EmptyVaList(emptyListAddress());
|
||||
|
||||
private final MemorySegment segment;
|
||||
private MemorySegment stack;
|
||||
private final MemorySegment gpRegsArea;
|
||||
private final long gpLimit;
|
||||
private final MemorySegment fpRegsArea;
|
||||
private final long fpLimit;
|
||||
|
||||
private LinuxAArch64VaList(MemorySegment segment, MemorySegment stack,
|
||||
MemorySegment gpRegsArea, long gpLimit, MemorySegment fpRegsArea, long fpLimit) {
|
||||
this.segment = segment;
|
||||
this.stack = stack;
|
||||
this.gpRegsArea = gpRegsArea;
|
||||
this.gpLimit = gpLimit;
|
||||
this.fpRegsArea = fpRegsArea;
|
||||
this.fpLimit = fpLimit;
|
||||
}
|
||||
|
||||
private static LinuxAArch64VaList readFromAddress(long address, SegmentScope scope) {
|
||||
MemorySegment segment = MemorySegment.ofAddress(address, LAYOUT.byteSize(), scope);
|
||||
MemorySegment stack = stackPtr(segment); // size unknown
|
||||
MemorySegment gpRegsArea = MemorySegment.ofAddress(grTop(segment).address() - MAX_GP_OFFSET, MAX_GP_OFFSET, scope);
|
||||
MemorySegment fpRegsArea = MemorySegment.ofAddress(vrTop(segment).address() - MAX_FP_OFFSET, MAX_FP_OFFSET, scope);
|
||||
return new LinuxAArch64VaList(segment, stack, gpRegsArea, MAX_GP_OFFSET, fpRegsArea, MAX_FP_OFFSET);
|
||||
}
|
||||
|
||||
private static MemorySegment emptyListAddress() {
|
||||
MemorySegment ms = MemorySegment.allocateNative(LAYOUT, SegmentScope.global());
|
||||
VH_stack.set(ms, MemorySegment.NULL);
|
||||
VH_gr_top.set(ms, MemorySegment.NULL);
|
||||
VH_vr_top.set(ms, MemorySegment.NULL);
|
||||
VH_gr_offs.set(ms, 0);
|
||||
VH_vr_offs.set(ms, 0);
|
||||
return ms.asSlice(0, 0);
|
||||
}
|
||||
|
||||
public static VaList empty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
private MemorySegment grTop() {
|
||||
return grTop(segment);
|
||||
}
|
||||
|
||||
private static MemorySegment grTop(MemorySegment segment) {
|
||||
return (MemorySegment) VH_gr_top.get(segment);
|
||||
}
|
||||
|
||||
private MemorySegment vrTop() {
|
||||
return vrTop(segment);
|
||||
}
|
||||
|
||||
private static MemorySegment vrTop(MemorySegment segment) {
|
||||
return (MemorySegment) VH_vr_top.get(segment);
|
||||
}
|
||||
|
||||
private int grOffs() {
|
||||
final int offs = (int) VH_gr_offs.get(segment);
|
||||
assert offs <= 0;
|
||||
return offs;
|
||||
}
|
||||
|
||||
private int vrOffs() {
|
||||
final int offs = (int) VH_vr_offs.get(segment);
|
||||
assert offs <= 0;
|
||||
return offs;
|
||||
}
|
||||
|
||||
private static MemorySegment stackPtr(MemorySegment segment) {
|
||||
return (MemorySegment) VH_stack.get(segment);
|
||||
}
|
||||
|
||||
private MemorySegment stackPtr() {
|
||||
return stackPtr(segment);
|
||||
}
|
||||
|
||||
private void setStack(MemorySegment newStack) {
|
||||
stack = newStack;
|
||||
VH_stack.set(segment, stack);
|
||||
}
|
||||
|
||||
private void consumeGPSlots(int num) {
|
||||
final int old = (int) VH_gr_offs.get(segment);
|
||||
VH_gr_offs.set(segment, old + num * GP_SLOT_SIZE);
|
||||
}
|
||||
|
||||
private void consumeFPSlots(int num) {
|
||||
final int old = (int) VH_vr_offs.get(segment);
|
||||
VH_vr_offs.set(segment, old + num * FP_SLOT_SIZE);
|
||||
}
|
||||
|
||||
private long currentGPOffset() {
|
||||
// Offset from start of GP register segment. __gr_top points to the top
|
||||
// (highest address) of the GP registers area. __gr_offs is the negative
|
||||
// offset of next saved register from the top.
|
||||
|
||||
return gpRegsArea.byteSize() + grOffs();
|
||||
}
|
||||
|
||||
private long currentFPOffset() {
|
||||
// Offset from start of FP register segment. __vr_top points to the top
|
||||
// (highest address) of the FP registers area. __vr_offs is the negative
|
||||
// offset of next saved register from the top.
|
||||
|
||||
return fpRegsArea.byteSize() + vrOffs();
|
||||
}
|
||||
|
||||
private long preAlignOffset(MemoryLayout layout) {
|
||||
long alignmentOffset = 0;
|
||||
if (layout.byteAlignment() > STACK_SLOT_SIZE) {
|
||||
long addr = stack.address();
|
||||
alignmentOffset = Utils.alignUp(addr, 16) - addr;
|
||||
}
|
||||
return alignmentOffset;
|
||||
}
|
||||
|
||||
private void preAlignStack(MemoryLayout layout) {
|
||||
setStack(stack.asSlice(preAlignOffset(layout)));
|
||||
}
|
||||
|
||||
private void postAlignStack(MemoryLayout layout) {
|
||||
setStack(stack.asSlice(Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextVarg(ValueLayout.OfInt layout) {
|
||||
return (int) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextVarg(ValueLayout.OfLong layout) {
|
||||
return (long) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextVarg(ValueLayout.OfDouble layout) {
|
||||
return (double) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(ValueLayout.OfAddress layout) {
|
||||
return (MemorySegment) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(allocator);
|
||||
return (MemorySegment) read( layout, allocator);
|
||||
}
|
||||
|
||||
private Object read(MemoryLayout layout) {
|
||||
return read(layout, THROWING_ALLOCATOR);
|
||||
}
|
||||
|
||||
private Object read(MemoryLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(layout);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass, layout)) {
|
||||
checkStackElement(layout);
|
||||
preAlignStack(layout);
|
||||
return switch (typeClass) {
|
||||
case STRUCT_REGISTER, STRUCT_HFA, STRUCT_REFERENCE -> {
|
||||
MemorySegment slice = stack.asSlice(0, layout.byteSize());
|
||||
MemorySegment seg = allocator.allocate(layout);
|
||||
seg.copyFrom(slice);
|
||||
postAlignStack(layout);
|
||||
yield seg;
|
||||
}
|
||||
case POINTER, INTEGER, FLOAT -> {
|
||||
VarHandle reader = layout.varHandle();
|
||||
MemorySegment slice = stack.asSlice(0, layout.byteSize());
|
||||
Object res = reader.get(slice);
|
||||
postAlignStack(layout);
|
||||
yield res;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return switch (typeClass) {
|
||||
case STRUCT_REGISTER -> {
|
||||
checkGPElement(layout, numSlots(layout));
|
||||
// Struct is passed packed in integer registers.
|
||||
MemorySegment value = allocator.allocate(layout);
|
||||
long offset = 0;
|
||||
while (offset < layout.byteSize()) {
|
||||
final long copy = Math.min(layout.byteSize() - offset, 8);
|
||||
MemorySegment.copy(gpRegsArea, currentGPOffset(), value, offset, copy);
|
||||
consumeGPSlots(1);
|
||||
offset += copy;
|
||||
}
|
||||
yield value;
|
||||
}
|
||||
case STRUCT_HFA -> {
|
||||
checkFPElement(layout, numSlots(layout));
|
||||
// Struct is passed with each element in a separate floating
|
||||
// point register.
|
||||
MemorySegment value = allocator.allocate(layout);
|
||||
GroupLayout group = (GroupLayout)layout;
|
||||
long offset = 0;
|
||||
for (MemoryLayout elem : group.memberLayouts()) {
|
||||
assert elem.byteSize() <= 8;
|
||||
final long copy = elem.byteSize();
|
||||
MemorySegment.copy(fpRegsArea, currentFPOffset(), value, offset, copy);
|
||||
consumeFPSlots(1);
|
||||
offset += copy;
|
||||
}
|
||||
yield value;
|
||||
}
|
||||
case STRUCT_REFERENCE -> {
|
||||
checkGPElement(layout, 1);
|
||||
// Struct is passed indirectly via a pointer in an integer register.
|
||||
VarHandle ptrReader = AArch64.C_POINTER.varHandle();
|
||||
MemorySegment ptr = (MemorySegment) ptrReader.get(
|
||||
gpRegsArea.asSlice(currentGPOffset()));
|
||||
consumeGPSlots(1);
|
||||
|
||||
MemorySegment slice = MemorySegment.ofAddress(ptr.address(), layout.byteSize(), segment.scope());
|
||||
MemorySegment seg = allocator.allocate(layout);
|
||||
seg.copyFrom(slice);
|
||||
yield seg;
|
||||
}
|
||||
case POINTER, INTEGER -> {
|
||||
checkGPElement(layout, 1);
|
||||
VarHandle reader = layout.varHandle();
|
||||
Object res = reader.get(gpRegsArea.asSlice(currentGPOffset()));
|
||||
consumeGPSlots(1);
|
||||
yield res;
|
||||
}
|
||||
case FLOAT -> {
|
||||
checkFPElement(layout, 1);
|
||||
VarHandle reader = layout.varHandle();
|
||||
Object res = reader.get(fpRegsArea.asSlice(currentFPOffset()));
|
||||
consumeFPSlots(1);
|
||||
yield res;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void checkGPElement(MemoryLayout layout, long slots) {
|
||||
if ((grOffs() + MAX_GP_OFFSET) + (slots * GP_SLOT_SIZE) > gpLimit) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkFPElement(MemoryLayout layout, long slots) {
|
||||
if ((vrOffs() + MAX_FP_OFFSET) + (slots * FP_SLOT_SIZE) > fpLimit) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkStackElement(MemoryLayout layout) {
|
||||
if (preAlignOffset(layout) + layout.byteSize() > stack.byteSize()) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(MemoryLayout... layouts) {
|
||||
Objects.requireNonNull(layouts);
|
||||
((MemorySessionImpl) segment.scope()).checkValidState();
|
||||
for (MemoryLayout layout : layouts) {
|
||||
Objects.requireNonNull(layout);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass, layout)) {
|
||||
checkStackElement(layout);
|
||||
preAlignStack(layout);
|
||||
postAlignStack(layout);
|
||||
} else if (typeClass == TypeClass.FLOAT || typeClass == TypeClass.STRUCT_HFA) {
|
||||
long slots = numSlots(layout);
|
||||
checkFPElement(layout, slots);
|
||||
consumeFPSlots((int) slots);
|
||||
} else if (typeClass == TypeClass.STRUCT_REFERENCE) {
|
||||
checkGPElement(layout, 1);
|
||||
consumeGPSlots(1);
|
||||
} else {
|
||||
long slots = numSlots(layout);
|
||||
checkGPElement(layout, slots);
|
||||
consumeGPSlots((int) slots);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static LinuxAArch64VaList.Builder builder(SegmentScope scope) {
|
||||
return new LinuxAArch64VaList.Builder(scope);
|
||||
}
|
||||
|
||||
public static VaList ofAddress(long address, SegmentScope scope) {
|
||||
return readFromAddress(address, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaList copy() {
|
||||
MemorySegment copy = MemorySegment.allocateNative(LAYOUT, segment.scope());
|
||||
copy.copyFrom(segment);
|
||||
return new LinuxAArch64VaList(copy, stack, gpRegsArea, gpLimit, fpRegsArea, fpLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment segment() {
|
||||
// make sure that returned segment cannot be accessed
|
||||
return segment.asSlice(0, 0);
|
||||
}
|
||||
|
||||
private static long numSlots(MemoryLayout layout) {
|
||||
return Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE) / STACK_SLOT_SIZE;
|
||||
}
|
||||
|
||||
private static boolean isRegOverflow(long currentGPOffset, long currentFPOffset,
|
||||
TypeClass typeClass, MemoryLayout layout) {
|
||||
if (typeClass == TypeClass.FLOAT || typeClass == TypeClass.STRUCT_HFA) {
|
||||
return currentFPOffset > MAX_FP_OFFSET - numSlots(layout) * FP_SLOT_SIZE;
|
||||
} else if (typeClass == TypeClass.STRUCT_REFERENCE) {
|
||||
return currentGPOffset > MAX_GP_OFFSET - GP_SLOT_SIZE;
|
||||
} else {
|
||||
return currentGPOffset > MAX_GP_OFFSET - numSlots(layout) * GP_SLOT_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LinuxAArch64VaList{"
|
||||
+ "__stack=" + stackPtr()
|
||||
+ ", __gr_top=" + grTop()
|
||||
+ ", __vr_top=" + vrTop()
|
||||
+ ", __gr_offs=" + grOffs()
|
||||
+ ", __vr_offs=" + vrOffs()
|
||||
+ '}';
|
||||
}
|
||||
|
||||
public static non-sealed class Builder implements VaList.Builder {
|
||||
private final SegmentScope scope;
|
||||
private final MemorySegment gpRegs;
|
||||
private final MemorySegment fpRegs;
|
||||
|
||||
private long currentGPOffset = 0;
|
||||
private long currentFPOffset = 0;
|
||||
private final List<SimpleVaArg> stackArgs = new ArrayList<>();
|
||||
|
||||
Builder(SegmentScope scope) {
|
||||
this.scope = scope;
|
||||
this.gpRegs = MemorySegment.allocateNative(LAYOUT_GP_REGS, scope);
|
||||
this.fpRegs = MemorySegment.allocateNative(LAYOUT_FP_REGS, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfInt layout, int value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfLong layout, long value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(GroupLayout layout, MemorySegment value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
private Builder arg(MemoryLayout layout, Object value) {
|
||||
Objects.requireNonNull(layout);
|
||||
Objects.requireNonNull(value);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
if (isRegOverflow(currentGPOffset, currentFPOffset, typeClass, layout)) {
|
||||
stackArgs.add(new SimpleVaArg(layout, value));
|
||||
} else {
|
||||
switch (typeClass) {
|
||||
case STRUCT_REGISTER -> {
|
||||
// Struct is passed packed in integer registers.
|
||||
MemorySegment valueSegment = (MemorySegment) value;
|
||||
long offset = 0;
|
||||
while (offset < layout.byteSize()) {
|
||||
final long copy = Math.min(layout.byteSize() - offset, 8);
|
||||
MemorySegment.copy(valueSegment, offset, gpRegs, currentGPOffset, copy);
|
||||
currentGPOffset += GP_SLOT_SIZE;
|
||||
offset += copy;
|
||||
}
|
||||
}
|
||||
case STRUCT_HFA -> {
|
||||
// Struct is passed with each element in a separate floating
|
||||
// point register.
|
||||
MemorySegment valueSegment = (MemorySegment) value;
|
||||
GroupLayout group = (GroupLayout)layout;
|
||||
long offset = 0;
|
||||
for (MemoryLayout elem : group.memberLayouts()) {
|
||||
assert elem.byteSize() <= 8;
|
||||
final long copy = elem.byteSize();
|
||||
MemorySegment.copy(valueSegment, offset, fpRegs, currentFPOffset, copy);
|
||||
currentFPOffset += FP_SLOT_SIZE;
|
||||
offset += copy;
|
||||
}
|
||||
}
|
||||
case STRUCT_REFERENCE -> {
|
||||
// Struct is passed indirectly via a pointer in an integer register.
|
||||
MemorySegment valueSegment = (MemorySegment) value;
|
||||
VarHandle writer = AArch64.C_POINTER.varHandle();
|
||||
writer.set(gpRegs.asSlice(currentGPOffset),
|
||||
valueSegment);
|
||||
currentGPOffset += GP_SLOT_SIZE;
|
||||
}
|
||||
case POINTER, INTEGER -> {
|
||||
VarHandle writer = layout.varHandle();
|
||||
writer.set(gpRegs.asSlice(currentGPOffset), value);
|
||||
currentGPOffset += GP_SLOT_SIZE;
|
||||
}
|
||||
case FLOAT -> {
|
||||
VarHandle writer = layout.varHandle();
|
||||
writer.set(fpRegs.asSlice(currentFPOffset), value);
|
||||
currentFPOffset += FP_SLOT_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private boolean isEmpty() {
|
||||
return currentGPOffset == 0 && currentFPOffset == 0 && stackArgs.isEmpty();
|
||||
}
|
||||
|
||||
public VaList build() {
|
||||
if (isEmpty()) {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
MemorySegment vaListSegment = MemorySegment.allocateNative(LAYOUT, scope);
|
||||
MemorySegment stackArgsSegment;
|
||||
if (!stackArgs.isEmpty()) {
|
||||
long stackArgsSize = stackArgs.stream()
|
||||
.reduce(0L, (acc, e) -> acc + Utils.alignUp(e.layout.byteSize(), STACK_SLOT_SIZE), Long::sum);
|
||||
stackArgsSegment = MemorySegment.allocateNative(stackArgsSize, 16, scope);
|
||||
MemorySegment writeCursor = stackArgsSegment;
|
||||
for (SimpleVaArg arg : stackArgs) {
|
||||
final long alignedSize = Utils.alignUp(arg.layout.byteSize(), STACK_SLOT_SIZE);
|
||||
writeCursor = Utils.alignUp(writeCursor, alignedSize);
|
||||
VarHandle writer = arg.varHandle();
|
||||
writer.set(writeCursor, arg.value);
|
||||
writeCursor = writeCursor.asSlice(alignedSize);
|
||||
}
|
||||
} else {
|
||||
stackArgsSegment = MemorySegment.NULL;
|
||||
}
|
||||
|
||||
VH_gr_top.set(vaListSegment, gpRegs.asSlice(gpRegs.byteSize()));
|
||||
VH_vr_top.set(vaListSegment, fpRegs.asSlice(fpRegs.byteSize()));
|
||||
VH_stack.set(vaListSegment, stackArgsSegment);
|
||||
VH_gr_offs.set(vaListSegment, -MAX_GP_OFFSET);
|
||||
VH_vr_offs.set(vaListSegment, -MAX_FP_OFFSET);
|
||||
|
||||
assert MemorySessionImpl.sameOwnerThread(gpRegs.scope(), vaListSegment.scope());
|
||||
assert MemorySessionImpl.sameOwnerThread(fpRegs.scope(), vaListSegment.scope());
|
||||
return new LinuxAArch64VaList(vaListSegment, stackArgsSegment, gpRegs, currentGPOffset, fpRegs, currentFPOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -30,11 +30,8 @@ import jdk.internal.foreign.abi.LinkerOptions;
|
||||
import jdk.internal.foreign.abi.aarch64.CallArranger;
|
||||
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.VaList;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* ABI implementation for macOS on Apple silicon. Based on AAPCS with
|
||||
@ -60,21 +57,7 @@ public final class MacOsAArch64Linker extends AbstractLinker {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function) {
|
||||
return CallArranger.MACOS.arrangeUpcall(targetType, function);
|
||||
}
|
||||
|
||||
public static VaList newVaList(Consumer<VaList.Builder> actions, SegmentScope scope) {
|
||||
MacOsAArch64VaList.Builder builder = MacOsAArch64VaList.builder(scope);
|
||||
actions.accept(builder);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static VaList newVaListOfAddress(long address, SegmentScope scope) {
|
||||
return MacOsAArch64VaList.ofAddress(address, scope);
|
||||
}
|
||||
|
||||
public static VaList emptyVaList() {
|
||||
return MacOsAArch64VaList.empty();
|
||||
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) {
|
||||
return CallArranger.MACOS.arrangeUpcall(targetType, function, options);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,257 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, Arm Limited. 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.aarch64.macos;
|
||||
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.SegmentAllocator;
|
||||
import java.lang.foreign.VaList;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import jdk.internal.foreign.abi.aarch64.TypeClass;
|
||||
import jdk.internal.foreign.MemorySessionImpl;
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
import jdk.internal.foreign.abi.SharedUtils.SimpleVaArg;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static jdk.internal.foreign.PlatformLayouts.AArch64.C_POINTER;
|
||||
import static jdk.internal.foreign.abi.SharedUtils.alignUp;
|
||||
|
||||
/**
|
||||
* Simplified va_list implementation used on macOS where all variadic
|
||||
* parameters are passed on the stack and the type of va_list decays to
|
||||
* char* instead of the structure defined in the AAPCS.
|
||||
*/
|
||||
public non-sealed class MacOsAArch64VaList implements VaList {
|
||||
private static final long VA_SLOT_SIZE_BYTES = 8;
|
||||
private static final VarHandle VH_address = C_POINTER.varHandle();
|
||||
|
||||
private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemorySegment.NULL);
|
||||
|
||||
private MemorySegment segment;
|
||||
|
||||
private MacOsAArch64VaList(MemorySegment segment) {
|
||||
this.segment = segment;
|
||||
}
|
||||
|
||||
public static VaList empty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextVarg(ValueLayout.OfInt layout) {
|
||||
return (int) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextVarg(ValueLayout.OfLong layout) {
|
||||
return (long) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextVarg(ValueLayout.OfDouble layout) {
|
||||
return (double) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(ValueLayout.OfAddress layout) {
|
||||
return (MemorySegment) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(allocator);
|
||||
return (MemorySegment) read(layout, allocator);
|
||||
}
|
||||
|
||||
private Object read(MemoryLayout layout) {
|
||||
return read(layout, SharedUtils.THROWING_ALLOCATOR);
|
||||
}
|
||||
|
||||
private Object read(MemoryLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(layout);
|
||||
Object res;
|
||||
if (layout instanceof GroupLayout) {
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
res = switch (typeClass) {
|
||||
case STRUCT_REFERENCE -> {
|
||||
checkElement(layout, VA_SLOT_SIZE_BYTES);
|
||||
MemorySegment structAddr = (MemorySegment) VH_address.get(segment);
|
||||
MemorySegment struct = MemorySegment.ofAddress(structAddr.address(), layout.byteSize(), segment.scope());
|
||||
MemorySegment seg = allocator.allocate(layout);
|
||||
seg.copyFrom(struct);
|
||||
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
yield seg;
|
||||
}
|
||||
case STRUCT_REGISTER, STRUCT_HFA -> {
|
||||
long size = alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES);
|
||||
checkElement(layout, size);
|
||||
MemorySegment struct = allocator.allocate(layout)
|
||||
.copyFrom(segment.asSlice(0, layout.byteSize()));
|
||||
segment = segment.asSlice(size);
|
||||
yield struct;
|
||||
}
|
||||
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
|
||||
};
|
||||
} else {
|
||||
checkElement(layout, VA_SLOT_SIZE_BYTES);
|
||||
VarHandle reader = layout.varHandle();
|
||||
res = reader.get(segment);
|
||||
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private static long sizeOf(MemoryLayout layout) {
|
||||
return switch (TypeClass.classifyLayout(layout)) {
|
||||
case STRUCT_REGISTER, STRUCT_HFA -> alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES);
|
||||
default -> VA_SLOT_SIZE_BYTES;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(MemoryLayout... layouts) {
|
||||
Objects.requireNonNull(layouts);
|
||||
((MemorySessionImpl) segment.scope()).checkValidState();
|
||||
|
||||
for (MemoryLayout layout : layouts) {
|
||||
Objects.requireNonNull(layout);
|
||||
long size = sizeOf(layout);
|
||||
checkElement(layout, size);
|
||||
segment = segment.asSlice(size);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkElement(MemoryLayout layout, long size) {
|
||||
if (segment.byteSize() < size) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
static MacOsAArch64VaList ofAddress(long address, SegmentScope session) {
|
||||
MemorySegment segment = MemorySegment.ofAddress(address, Long.MAX_VALUE, session);
|
||||
return new MacOsAArch64VaList(segment);
|
||||
}
|
||||
|
||||
static Builder builder(SegmentScope session) {
|
||||
return new Builder(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaList copy() {
|
||||
((MemorySessionImpl) segment.scope()).checkValidState();
|
||||
return new MacOsAArch64VaList(segment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment segment() {
|
||||
// make sure that returned segment cannot be accessed
|
||||
return segment.asSlice(0, 0);
|
||||
}
|
||||
|
||||
public static non-sealed class Builder implements VaList.Builder {
|
||||
|
||||
private final SegmentScope session;
|
||||
private final List<SimpleVaArg> args = new ArrayList<>();
|
||||
|
||||
public Builder(SegmentScope session) {
|
||||
((MemorySessionImpl) session).checkValidState();
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
private Builder arg(MemoryLayout layout, Object value) {
|
||||
Objects.requireNonNull(layout);
|
||||
Objects.requireNonNull(value);
|
||||
args.add(new SimpleVaArg(layout, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfInt layout, int value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfLong layout, long value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(GroupLayout layout, MemorySegment value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
public VaList build() {
|
||||
if (args.isEmpty()) {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
long allocationSize = args.stream().reduce(0L, (acc, e) -> acc + sizeOf(e.layout), Long::sum);
|
||||
MemorySegment segment = MemorySegment.allocateNative(allocationSize, session);
|
||||
MemorySegment cursor = segment;
|
||||
|
||||
for (SimpleVaArg arg : args) {
|
||||
if (arg.layout instanceof GroupLayout) {
|
||||
MemorySegment msArg = ((MemorySegment) arg.value);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(arg.layout);
|
||||
switch (typeClass) {
|
||||
case STRUCT_REFERENCE -> {
|
||||
MemorySegment copy = MemorySegment.allocateNative(arg.layout, session);
|
||||
copy.copyFrom(msArg); // by-value
|
||||
VH_address.set(cursor, copy);
|
||||
cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
}
|
||||
case STRUCT_REGISTER, STRUCT_HFA ->
|
||||
cursor.copyFrom(msArg.asSlice(0, arg.layout.byteSize()))
|
||||
.asSlice(alignUp(arg.layout.byteSize(), VA_SLOT_SIZE_BYTES));
|
||||
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
|
||||
}
|
||||
} else {
|
||||
VarHandle writer = arg.varHandle();
|
||||
writer.set(cursor, arg.value);
|
||||
cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
}
|
||||
}
|
||||
|
||||
return new MacOsAArch64VaList(segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -31,11 +31,8 @@ import jdk.internal.foreign.abi.LinkerOptions;
|
||||
import jdk.internal.foreign.abi.aarch64.CallArranger;
|
||||
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.VaList;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* ABI implementation for Windows/AArch64. Based on AAPCS with
|
||||
@ -57,22 +54,7 @@ public final class WindowsAArch64Linker extends AbstractLinker {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function) {
|
||||
return CallArranger.WINDOWS.arrangeUpcall(targetType, function);
|
||||
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) {
|
||||
return CallArranger.WINDOWS.arrangeUpcall(targetType, function, options);
|
||||
}
|
||||
|
||||
public static VaList newVaList(Consumer<VaList.Builder> actions, SegmentScope scope) {
|
||||
WindowsAArch64VaList.Builder builder = WindowsAArch64VaList.builder(scope);
|
||||
actions.accept(builder);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static VaList newVaListOfAddress(long address, SegmentScope scope) {
|
||||
return WindowsAArch64VaList.ofAddress(address, scope);
|
||||
}
|
||||
|
||||
public static VaList emptyVaList() {
|
||||
return WindowsAArch64VaList.empty();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,261 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, Arm Limited. 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.aarch64.windows;
|
||||
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.SegmentAllocator;
|
||||
import java.lang.foreign.VaList;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import jdk.internal.foreign.abi.aarch64.TypeClass;
|
||||
import jdk.internal.foreign.MemorySessionImpl;
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
import jdk.internal.foreign.abi.SharedUtils.SimpleVaArg;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static jdk.internal.foreign.PlatformLayouts.AArch64.C_POINTER;
|
||||
import static jdk.internal.foreign.abi.SharedUtils.alignUp;
|
||||
|
||||
// see vadefs.h (VC header) for the ARM64 va_arg impl
|
||||
//
|
||||
// typedef char* va_list;
|
||||
//
|
||||
// #define __crt_va_arg(ap, t) \
|
||||
// ((sizeof(t) > (2 * sizeof(__int64))) \
|
||||
// ? **(t**)((ap += sizeof(__int64)) - sizeof(__int64)) \
|
||||
// : *(t*)((ap += _SLOTSIZEOF(t) + _APALIGN(t,ap)) - _SLOTSIZEOF(t)))
|
||||
//
|
||||
public non-sealed class WindowsAArch64VaList implements VaList {
|
||||
private static final long VA_SLOT_SIZE_BYTES = 8;
|
||||
private static final VarHandle VH_address = C_POINTER.varHandle();
|
||||
|
||||
private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemorySegment.NULL);
|
||||
|
||||
private MemorySegment segment;
|
||||
|
||||
private WindowsAArch64VaList(MemorySegment segment) {
|
||||
this.segment = segment;
|
||||
}
|
||||
|
||||
public static VaList empty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextVarg(ValueLayout.OfInt layout) {
|
||||
return (int) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextVarg(ValueLayout.OfLong layout) {
|
||||
return (long) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextVarg(ValueLayout.OfDouble layout) {
|
||||
return (double) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(ValueLayout.OfAddress layout) {
|
||||
return (MemorySegment) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(allocator);
|
||||
return (MemorySegment) read(layout, allocator);
|
||||
}
|
||||
|
||||
private Object read(MemoryLayout layout) {
|
||||
return read(layout, SharedUtils.THROWING_ALLOCATOR);
|
||||
}
|
||||
|
||||
private Object read(MemoryLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(layout);
|
||||
Object res;
|
||||
if (layout instanceof GroupLayout) {
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
res = switch (typeClass) {
|
||||
case STRUCT_REFERENCE -> {
|
||||
checkElement(layout, VA_SLOT_SIZE_BYTES);
|
||||
MemorySegment structAddr = (MemorySegment) VH_address.get(segment);
|
||||
MemorySegment struct = MemorySegment.ofAddress(structAddr.address(), layout.byteSize(), segment.scope());
|
||||
MemorySegment seg = allocator.allocate(layout);
|
||||
seg.copyFrom(struct);
|
||||
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
yield seg;
|
||||
}
|
||||
case STRUCT_REGISTER, STRUCT_HFA -> {
|
||||
long size = alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES);
|
||||
checkElement(layout, size);
|
||||
MemorySegment struct = allocator.allocate(layout)
|
||||
.copyFrom(segment.asSlice(0, layout.byteSize()));
|
||||
segment = segment.asSlice(size);
|
||||
yield struct;
|
||||
}
|
||||
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
|
||||
};
|
||||
} else {
|
||||
checkElement(layout, VA_SLOT_SIZE_BYTES);
|
||||
VarHandle reader = layout.varHandle();
|
||||
res = reader.get(segment);
|
||||
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private static long sizeOf(MemoryLayout layout) {
|
||||
return switch (TypeClass.classifyLayout(layout)) {
|
||||
case STRUCT_REGISTER, STRUCT_HFA -> alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES);
|
||||
default -> VA_SLOT_SIZE_BYTES;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(MemoryLayout... layouts) {
|
||||
Objects.requireNonNull(layouts);
|
||||
((MemorySessionImpl) segment.scope()).checkValidState();
|
||||
|
||||
for (MemoryLayout layout : layouts) {
|
||||
Objects.requireNonNull(layout);
|
||||
long size = sizeOf(layout);
|
||||
checkElement(layout, size);
|
||||
segment = segment.asSlice(size);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkElement(MemoryLayout layout, long size) {
|
||||
if (segment.byteSize() < size) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
static WindowsAArch64VaList ofAddress(long address, SegmentScope session) {
|
||||
MemorySegment segment = MemorySegment.ofAddress(address, Long.MAX_VALUE, session);
|
||||
return new WindowsAArch64VaList(segment);
|
||||
}
|
||||
|
||||
static Builder builder(SegmentScope session) {
|
||||
return new Builder(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaList copy() {
|
||||
((MemorySessionImpl) segment.scope()).checkValidState();
|
||||
return new WindowsAArch64VaList(segment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment segment() {
|
||||
// make sure that returned segment cannot be accessed
|
||||
return segment.asSlice(0, 0);
|
||||
}
|
||||
|
||||
public static non-sealed class Builder implements VaList.Builder {
|
||||
|
||||
private final SegmentScope session;
|
||||
private final List<SimpleVaArg> args = new ArrayList<>();
|
||||
|
||||
public Builder(SegmentScope session) {
|
||||
((MemorySessionImpl) session).checkValidState();
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
private Builder arg(MemoryLayout layout, Object value) {
|
||||
Objects.requireNonNull(layout);
|
||||
Objects.requireNonNull(value);
|
||||
args.add(new SimpleVaArg(layout, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfInt layout, int value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfLong layout, long value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(GroupLayout layout, MemorySegment value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
public VaList build() {
|
||||
if (args.isEmpty()) {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
long allocationSize = args.stream().reduce(0L, (acc, e) -> acc + sizeOf(e.layout), Long::sum);
|
||||
MemorySegment segment = MemorySegment.allocateNative(allocationSize, session);
|
||||
MemorySegment cursor = segment;
|
||||
|
||||
for (SimpleVaArg arg : args) {
|
||||
if (arg.layout instanceof GroupLayout) {
|
||||
MemorySegment msArg = ((MemorySegment) arg.value);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(arg.layout);
|
||||
switch (typeClass) {
|
||||
case STRUCT_REFERENCE -> {
|
||||
MemorySegment copy = MemorySegment.allocateNative(arg.layout, session);
|
||||
copy.copyFrom(msArg); // by-value
|
||||
VH_address.set(cursor, copy);
|
||||
cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
}
|
||||
case STRUCT_REGISTER, STRUCT_HFA ->
|
||||
cursor = cursor.copyFrom(msArg.asSlice(0, arg.layout.byteSize()))
|
||||
.asSlice(alignUp(arg.layout.byteSize(), VA_SLOT_SIZE_BYTES));
|
||||
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
|
||||
}
|
||||
} else {
|
||||
VarHandle writer = arg.varHandle();
|
||||
writer.set(cursor, arg.value);
|
||||
cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
}
|
||||
}
|
||||
|
||||
return new WindowsAArch64VaList(segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.internal.foreign.abi.fallback;
|
||||
|
||||
/**
|
||||
* enum which maps the {@code ffi_abi} enum
|
||||
*/
|
||||
enum FFIABI {
|
||||
DEFAULT(LibFallback.defaultABI());
|
||||
|
||||
private final int value;
|
||||
|
||||
FFIABI(int abi) {
|
||||
this.value = abi;
|
||||
}
|
||||
|
||||
int value() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.internal.foreign.abi.fallback;
|
||||
|
||||
/**
|
||||
* See doc: <a href="https://github.com/libffi/libffi/blob/7611bb4cfe90884b55ad225e0166136a1d2cf22b/doc/libffi.texi#L159"></a>
|
||||
* <p>
|
||||
* typedef enum {
|
||||
* FFI_OK = 0,
|
||||
* FFI_BAD_TYPEDEF,
|
||||
* FFI_BAD_ABI,
|
||||
* FFI_BAD_ARGTYPE
|
||||
* } ffi_status;
|
||||
*/
|
||||
enum FFIStatus {
|
||||
FFI_OK,
|
||||
FFI_BAD_TYPEDEF,
|
||||
FFI_BAD_ABI,
|
||||
FFI_BAD_ARGTYPE;
|
||||
|
||||
static FFIStatus of(int code) {
|
||||
return switch (code) {
|
||||
case 0 -> FFI_OK;
|
||||
case 1 -> FFI_BAD_TYPEDEF;
|
||||
case 2 -> FFI_BAD_ABI;
|
||||
case 3 -> FFI_BAD_ARGTYPE;
|
||||
default -> throw new IllegalArgumentException("Unknown status code: " + code);
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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.internal.foreign.abi.fallback;
|
||||
|
||||
import jdk.internal.foreign.Utils;
|
||||
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.PaddingLayout;
|
||||
import java.lang.foreign.SequenceLayout;
|
||||
import java.lang.foreign.StructLayout;
|
||||
import java.lang.foreign.UnionLayout;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static java.lang.foreign.ValueLayout.ADDRESS;
|
||||
import static java.lang.foreign.ValueLayout.JAVA_INT;
|
||||
import static java.lang.foreign.ValueLayout.JAVA_LONG;
|
||||
import static java.lang.foreign.ValueLayout.JAVA_SHORT;
|
||||
|
||||
/**
|
||||
* typedef struct _ffi_type
|
||||
* {
|
||||
* size_t size;
|
||||
* unsigned short alignment;
|
||||
* unsigned short type;
|
||||
* struct _ffi_type **elements;
|
||||
* } ffi_type;
|
||||
*/
|
||||
class FFIType {
|
||||
private static final ValueLayout SIZE_T = switch ((int) ADDRESS.bitSize()) {
|
||||
case 64 -> JAVA_LONG;
|
||||
case 32 -> JAVA_INT;
|
||||
default -> throw new IllegalStateException("Address size not supported: " + ADDRESS.byteSize());
|
||||
};
|
||||
private static final ValueLayout UNSIGNED_SHORT = JAVA_SHORT;
|
||||
private static final StructLayout LAYOUT = Utils.computePaddedStructLayout(
|
||||
SIZE_T, UNSIGNED_SHORT, UNSIGNED_SHORT.withName("type"), ADDRESS.withName("elements"));
|
||||
|
||||
private static final VarHandle VH_TYPE = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("type"));
|
||||
private static final VarHandle VH_ELEMENTS = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("elements"));
|
||||
private static final VarHandle VH_SIZE_T_ARRAY = SIZE_T.arrayElementVarHandle();
|
||||
|
||||
private static MemorySegment make(List<MemoryLayout> elements, FFIABI abi, Arena scope) {
|
||||
MemorySegment elementsSeg = scope.allocate((elements.size() + 1) * ADDRESS.byteSize());
|
||||
int i = 0;
|
||||
for (; i < elements.size(); i++) {
|
||||
MemoryLayout elementLayout = elements.get(i);
|
||||
MemorySegment elementType = toFFIType(elementLayout, abi, scope);
|
||||
elementsSeg.setAtIndex(ADDRESS, i, elementType);
|
||||
}
|
||||
// elements array is null-terminated
|
||||
elementsSeg.setAtIndex(ADDRESS, i, MemorySegment.NULL);
|
||||
|
||||
MemorySegment ffiType = scope.allocate(LAYOUT);
|
||||
VH_TYPE.set(ffiType, LibFallback.structTag());
|
||||
VH_ELEMENTS.set(ffiType, elementsSeg);
|
||||
|
||||
return ffiType;
|
||||
}
|
||||
|
||||
private static final Map<Class<?>, MemorySegment> CARRIER_TO_TYPE = Map.of(
|
||||
boolean.class, LibFallback.uint8Type(),
|
||||
byte.class, LibFallback.sint8Type(),
|
||||
short.class, LibFallback.sint16Type(),
|
||||
char.class, LibFallback.uint16Type(),
|
||||
int.class, LibFallback.sint32Type(),
|
||||
long.class, LibFallback.sint64Type(),
|
||||
float.class, LibFallback.floatType(),
|
||||
double.class, LibFallback.doubleType(),
|
||||
MemorySegment.class, LibFallback.pointerType()
|
||||
);
|
||||
|
||||
static MemorySegment toFFIType(MemoryLayout layout, FFIABI abi, Arena scope) {
|
||||
if (layout instanceof GroupLayout grpl) {
|
||||
if (grpl instanceof StructLayout strl) {
|
||||
// libffi doesn't want our padding
|
||||
List<MemoryLayout> filteredLayouts = strl.memberLayouts().stream()
|
||||
.filter(Predicate.not(PaddingLayout.class::isInstance))
|
||||
.toList();
|
||||
MemorySegment structType = make(filteredLayouts, abi, scope);
|
||||
verifyStructType(strl, filteredLayouts, structType, abi);
|
||||
return structType;
|
||||
}
|
||||
assert grpl instanceof UnionLayout;
|
||||
// JDK-8301800
|
||||
throw new IllegalArgumentException("Fallback linker does not support by-value unions: " + grpl);
|
||||
} else if (layout instanceof SequenceLayout sl) {
|
||||
List<MemoryLayout> elements = Collections.nCopies(Math.toIntExact(sl.elementCount()), sl.elementLayout());
|
||||
return make(elements, abi, scope);
|
||||
}
|
||||
return Objects.requireNonNull(CARRIER_TO_TYPE.get(((ValueLayout) layout).carrier()));
|
||||
}
|
||||
|
||||
// verify layout against what libffi sets
|
||||
private static void verifyStructType(StructLayout structLayout, List<MemoryLayout> filteredLayouts, MemorySegment structType,
|
||||
FFIABI abi) {
|
||||
try (Arena verifyArena = Arena.ofConfined()) {
|
||||
MemorySegment offsetsOut = verifyArena.allocate(SIZE_T.byteSize() * filteredLayouts.size());
|
||||
LibFallback.getStructOffsets(structType, offsetsOut, abi);
|
||||
long expectedOffset = 0;
|
||||
int offsetIdx = 0;
|
||||
for (MemoryLayout element : structLayout.memberLayouts()) {
|
||||
if (!(element instanceof PaddingLayout)) {
|
||||
long ffiOffset = (long) VH_SIZE_T_ARRAY.get(offsetsOut, offsetIdx++);
|
||||
if (ffiOffset != expectedOffset) {
|
||||
throw new IllegalArgumentException("Invalid group layout." +
|
||||
" Offset of '" + element.name().orElse("<unnamed>")
|
||||
+ "': " + expectedOffset + " != " + ffiOffset);
|
||||
}
|
||||
}
|
||||
expectedOffset += element.byteSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,272 @@
|
||||
/*
|
||||
* 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.internal.foreign.abi.fallback;
|
||||
|
||||
import jdk.internal.foreign.AbstractMemorySegmentImpl;
|
||||
import jdk.internal.foreign.MemorySessionImpl;
|
||||
import jdk.internal.foreign.abi.AbstractLinker;
|
||||
import jdk.internal.foreign.abi.CapturableState;
|
||||
import jdk.internal.foreign.abi.LinkerOptions;
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
|
||||
import java.lang.foreign.AddressLayout;
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentAllocator;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.ref.Reference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static java.lang.foreign.ValueLayout.ADDRESS;
|
||||
import static java.lang.foreign.ValueLayout.JAVA_LONG;
|
||||
import static java.lang.invoke.MethodHandles.foldArguments;
|
||||
|
||||
public final class FallbackLinker extends AbstractLinker {
|
||||
|
||||
private static final MethodHandle MH_DO_DOWNCALL;
|
||||
private static final MethodHandle MH_DO_UPCALL;
|
||||
|
||||
static {
|
||||
try {
|
||||
MH_DO_DOWNCALL = MethodHandles.lookup().findStatic(FallbackLinker.class, "doDowncall",
|
||||
MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class, FallbackLinker.DowncallData.class));
|
||||
MH_DO_UPCALL = MethodHandles.lookup().findStatic(FallbackLinker.class, "doUpcall",
|
||||
MethodType.methodType(void.class, MethodHandle.class, MemorySegment.class, MemorySegment.class, UpcallData.class));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new ExceptionInInitializerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static FallbackLinker getInstance() {
|
||||
class Holder {
|
||||
static final FallbackLinker INSTANCE = new FallbackLinker();
|
||||
}
|
||||
return Holder.INSTANCE;
|
||||
}
|
||||
|
||||
public static boolean isSupported() {
|
||||
return LibFallback.SUPPORTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function, LinkerOptions options) {
|
||||
MemorySegment cif = makeCif(inferredMethodType, function, FFIABI.DEFAULT, Arena.ofAuto());
|
||||
|
||||
int capturedStateMask = options.capturedCallState()
|
||||
.mapToInt(CapturableState::mask)
|
||||
.reduce(0, (a, b) -> a | b);
|
||||
DowncallData invData = new DowncallData(cif, function.returnLayout().orElse(null),
|
||||
function.argumentLayouts(), capturedStateMask);
|
||||
|
||||
MethodHandle target = MethodHandles.insertArguments(MH_DO_DOWNCALL, 2, invData);
|
||||
|
||||
int leadingArguments = 1; // address
|
||||
MethodType type = inferredMethodType.insertParameterTypes(0, SegmentAllocator.class, MemorySegment.class);
|
||||
if (capturedStateMask != 0) {
|
||||
leadingArguments++;
|
||||
type = type.insertParameterTypes(2, MemorySegment.class);
|
||||
}
|
||||
target = target.asCollector(1, Object[].class, inferredMethodType.parameterCount() + leadingArguments);
|
||||
target = target.asType(type);
|
||||
target = foldArguments(target, 1, SharedUtils.MH_CHECK_SYMBOL);
|
||||
target = SharedUtils.swapArguments(target, 0, 1); // normalize parameter order
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) {
|
||||
MemorySegment cif = makeCif(targetType, function, FFIABI.DEFAULT, Arena.ofAuto());
|
||||
|
||||
UpcallData invData = new UpcallData(function.returnLayout().orElse(null), function.argumentLayouts(), cif);
|
||||
MethodHandle doUpcallMH = MethodHandles.insertArguments(MH_DO_UPCALL, 3, invData);
|
||||
|
||||
return (target, scope) -> {
|
||||
target = MethodHandles.insertArguments(doUpcallMH, 0, target);
|
||||
return LibFallback.createClosure(cif, target, scope);
|
||||
};
|
||||
}
|
||||
|
||||
private static MemorySegment makeCif(MethodType methodType, FunctionDescriptor function, FFIABI abi, Arena scope) {
|
||||
MemorySegment argTypes = scope.allocate(function.argumentLayouts().size() * ADDRESS.byteSize());
|
||||
List<MemoryLayout> argLayouts = function.argumentLayouts();
|
||||
for (int i = 0; i < argLayouts.size(); i++) {
|
||||
MemoryLayout layout = argLayouts.get(i);
|
||||
argTypes.setAtIndex(ADDRESS, i, FFIType.toFFIType(layout, abi, scope));
|
||||
}
|
||||
|
||||
MemorySegment returnType = methodType.returnType() != void.class
|
||||
? FFIType.toFFIType(function.returnLayout().orElseThrow(), abi, scope)
|
||||
: LibFallback.voidType();
|
||||
return LibFallback.prepCif(returnType, argLayouts.size(), argTypes, abi, scope);
|
||||
}
|
||||
|
||||
private record DowncallData(MemorySegment cif, MemoryLayout returnLayout, List<MemoryLayout> argLayouts,
|
||||
int capturedStateMask) {}
|
||||
|
||||
private static Object doDowncall(SegmentAllocator returnAllocator, Object[] args, DowncallData invData) {
|
||||
List<MemorySessionImpl> acquiredSessions = new ArrayList<>();
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
int argStart = 0;
|
||||
|
||||
MemorySegment target = (MemorySegment) args[argStart++];
|
||||
MemorySessionImpl targetImpl = ((AbstractMemorySegmentImpl) target).sessionImpl();
|
||||
targetImpl.acquire0();
|
||||
acquiredSessions.add(targetImpl);
|
||||
|
||||
MemorySegment capturedState = null;
|
||||
if (invData.capturedStateMask() != 0) {
|
||||
capturedState = (MemorySegment) args[argStart++];
|
||||
MemorySessionImpl capturedStateImpl = ((AbstractMemorySegmentImpl) capturedState).sessionImpl();
|
||||
capturedStateImpl.acquire0();
|
||||
acquiredSessions.add(capturedStateImpl);
|
||||
}
|
||||
|
||||
List<MemoryLayout> argLayouts = invData.argLayouts();
|
||||
MemorySegment argPtrs = arena.allocate(argLayouts.size() * ADDRESS.byteSize());
|
||||
for (int i = 0; i < argLayouts.size(); i++) {
|
||||
Object arg = args[argStart + i];
|
||||
MemoryLayout layout = argLayouts.get(i);
|
||||
MemorySegment argSeg = arena.allocate(layout);
|
||||
writeValue(arg, layout, argSeg, addr -> {
|
||||
MemorySessionImpl sessionImpl = ((AbstractMemorySegmentImpl) addr).sessionImpl();
|
||||
sessionImpl.acquire0();
|
||||
acquiredSessions.add(sessionImpl);
|
||||
});
|
||||
argPtrs.setAtIndex(ADDRESS, i, argSeg);
|
||||
}
|
||||
|
||||
MemorySegment retSeg = null;
|
||||
if (invData.returnLayout() != null) {
|
||||
retSeg = (invData.returnLayout() instanceof GroupLayout ? returnAllocator : arena).allocate(invData.returnLayout);
|
||||
}
|
||||
|
||||
LibFallback.doDowncall(invData.cif, target, retSeg, argPtrs, capturedState, invData.capturedStateMask());
|
||||
|
||||
Reference.reachabilityFence(invData.cif());
|
||||
|
||||
return readValue(retSeg, invData.returnLayout());
|
||||
} finally {
|
||||
for (MemorySessionImpl session : acquiredSessions) {
|
||||
session.release0();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// note that cif is not used, but we store it here to keep it alive
|
||||
private record UpcallData(MemoryLayout returnLayout, List<MemoryLayout> argLayouts, MemorySegment cif) {}
|
||||
|
||||
private static void doUpcall(MethodHandle target, MemorySegment retPtr, MemorySegment argPtrs, UpcallData data) throws Throwable {
|
||||
List<MemoryLayout> argLayouts = data.argLayouts();
|
||||
int numArgs = argLayouts.size();
|
||||
MemoryLayout retLayout = data.returnLayout();
|
||||
try (Arena upcallArena = Arena.ofConfined()) {
|
||||
MemorySegment argsSeg = argPtrs.reinterpret(numArgs * ADDRESS.byteSize(), upcallArena, null);
|
||||
MemorySegment retSeg = retLayout != null
|
||||
? retPtr.reinterpret(retLayout.byteSize(), upcallArena, null)
|
||||
: null;
|
||||
|
||||
Object[] args = new Object[numArgs];
|
||||
for (int i = 0; i < numArgs; i++) {
|
||||
MemoryLayout argLayout = argLayouts.get(i);
|
||||
MemorySegment argPtr = argsSeg.getAtIndex(ADDRESS, i)
|
||||
.reinterpret(argLayout.byteSize(), upcallArena, null);
|
||||
args[i] = readValue(argPtr, argLayout);
|
||||
}
|
||||
|
||||
Object result = target.invokeWithArguments(args);
|
||||
|
||||
writeValue(result, data.returnLayout(), retSeg);
|
||||
}
|
||||
}
|
||||
|
||||
// where
|
||||
private static void writeValue(Object arg, MemoryLayout layout, MemorySegment argSeg) {
|
||||
writeValue(arg, layout, argSeg, addr -> {});
|
||||
}
|
||||
|
||||
private static void writeValue(Object arg, MemoryLayout layout, MemorySegment argSeg,
|
||||
Consumer<MemorySegment> acquireCallback) {
|
||||
if (layout instanceof ValueLayout.OfBoolean bl) {
|
||||
argSeg.set(bl, 0, (Boolean) arg);
|
||||
} else if (layout instanceof ValueLayout.OfByte bl) {
|
||||
argSeg.set(bl, 0, (Byte) arg);
|
||||
} else if (layout instanceof ValueLayout.OfShort sl) {
|
||||
argSeg.set(sl, 0, (Short) arg);
|
||||
} else if (layout instanceof ValueLayout.OfChar cl) {
|
||||
argSeg.set(cl, 0, (Character) arg);
|
||||
} else if (layout instanceof ValueLayout.OfInt il) {
|
||||
argSeg.set(il, 0, (Integer) arg);
|
||||
} else if (layout instanceof ValueLayout.OfLong ll) {
|
||||
argSeg.set(ll, 0, (Long) arg);
|
||||
} else if (layout instanceof ValueLayout.OfFloat fl) {
|
||||
argSeg.set(fl, 0, (Float) arg);
|
||||
} else if (layout instanceof ValueLayout.OfDouble dl) {
|
||||
argSeg.set(dl, 0, (Double) arg);
|
||||
} else if (layout instanceof AddressLayout al) {
|
||||
MemorySegment addrArg = (MemorySegment) arg;
|
||||
acquireCallback.accept(addrArg);
|
||||
argSeg.set(al, 0, addrArg);
|
||||
} else if (layout instanceof GroupLayout) {
|
||||
argSeg.copyFrom((MemorySegment) arg); // by-value struct
|
||||
} else {
|
||||
assert layout == null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Object readValue(MemorySegment seg, MemoryLayout layout) {
|
||||
if (layout instanceof ValueLayout.OfBoolean bl) {
|
||||
return seg.get(bl, 0);
|
||||
} else if (layout instanceof ValueLayout.OfByte bl) {
|
||||
return seg.get(bl, 0);
|
||||
} else if (layout instanceof ValueLayout.OfShort sl) {
|
||||
return seg.get(sl, 0);
|
||||
} else if (layout instanceof ValueLayout.OfChar cl) {
|
||||
return seg.get(cl, 0);
|
||||
} else if (layout instanceof ValueLayout.OfInt il) {
|
||||
return seg.get(il, 0);
|
||||
} else if (layout instanceof ValueLayout.OfLong ll) {
|
||||
return seg.get(ll, 0);
|
||||
} else if (layout instanceof ValueLayout.OfFloat fl) {
|
||||
return seg.get(fl, 0);
|
||||
} else if (layout instanceof ValueLayout.OfDouble dl) {
|
||||
return seg.get(dl, 0);
|
||||
} else if (layout instanceof AddressLayout al) {
|
||||
return seg.get(al, 0);
|
||||
} else if (layout instanceof GroupLayout) {
|
||||
return seg;
|
||||
}
|
||||
assert layout == null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,219 @@
|
||||
/*
|
||||
* 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.internal.foreign.abi.fallback;
|
||||
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
|
||||
final class LibFallback {
|
||||
private LibFallback() {}
|
||||
|
||||
static final boolean SUPPORTED = tryLoadLibrary();
|
||||
|
||||
private static boolean tryLoadLibrary() {
|
||||
try {
|
||||
System.loadLibrary("fallbackLinker");
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
return false;
|
||||
}
|
||||
init();
|
||||
return true;
|
||||
}
|
||||
|
||||
static int defaultABI() { return NativeConstants.DEFAULT_ABI; }
|
||||
|
||||
static MemorySegment uint8Type() { return NativeConstants.UINT8_TYPE; }
|
||||
static MemorySegment sint8Type() { return NativeConstants.SINT8_TYPE; }
|
||||
static MemorySegment uint16Type() { return NativeConstants.UINT16_TYPE; }
|
||||
static MemorySegment sint16Type() { return NativeConstants.SINT16_TYPE; }
|
||||
static MemorySegment sint32Type() { return NativeConstants.SINT32_TYPE; }
|
||||
static MemorySegment sint64Type() { return NativeConstants.SINT64_TYPE; }
|
||||
static MemorySegment floatType() { return NativeConstants.FLOAT_TYPE; }
|
||||
static MemorySegment doubleType() { return NativeConstants.DOUBLE_TYPE; }
|
||||
static MemorySegment pointerType() { return NativeConstants.POINTER_TYPE; }
|
||||
static MemorySegment voidType() { return NativeConstants.VOID_TYPE; }
|
||||
|
||||
static short structTag() { return NativeConstants.STRUCT_TAG; }
|
||||
|
||||
private static final MethodType UPCALL_TARGET_TYPE = MethodType.methodType(void.class, MemorySegment.class, MemorySegment.class);
|
||||
|
||||
/**
|
||||
* Do a libffi based downcall. This method wraps the {@code ffi_call} function
|
||||
*
|
||||
* @param cif a pointer to a {@code ffi_cif} struct
|
||||
* @param target the address of the target function
|
||||
* @param retPtr a pointer to a buffer into which the return value shall be written, or {@code null} if the target
|
||||
* function does not return a value
|
||||
* @param argPtrs a pointer to an array of pointers, which each point to an argument value
|
||||
* @param capturedState a pointer to a buffer into which captured state is written, or {@code null} if no state is
|
||||
* to be captured
|
||||
* @param capturedStateMask the bit mask indicating which state to capture
|
||||
*
|
||||
* @see jdk.internal.foreign.abi.CapturableState
|
||||
*/
|
||||
static void doDowncall(MemorySegment cif, MemorySegment target, MemorySegment retPtr, MemorySegment argPtrs,
|
||||
MemorySegment capturedState, int capturedStateMask) {
|
||||
doDowncall(cif.address(), target.address(),
|
||||
retPtr == null ? 0 : retPtr.address(), argPtrs.address(),
|
||||
capturedState == null ? 0 : capturedState.address(), capturedStateMask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for {@code ffi_prep_cif}
|
||||
*
|
||||
* @param returnType a pointer to an @{code ffi_type} describing the return type
|
||||
* @param numArgs the number of arguments
|
||||
* @param paramTypes a pointer to an array of pointers, which each point to an {@code ffi_type} describing a
|
||||
* parameter type
|
||||
* @param abi the abi to be used
|
||||
* @param scope the scope into which to allocate the returned {@code ffi_cif} struct
|
||||
* @return a pointer to a prepared {@code ffi_cif} struct
|
||||
*
|
||||
* @throws IllegalStateException if the call to {@code ffi_prep_cif} returns a non-zero status code
|
||||
*/
|
||||
static MemorySegment prepCif(MemorySegment returnType, int numArgs, MemorySegment paramTypes, FFIABI abi,
|
||||
Arena scope) throws IllegalStateException {
|
||||
MemorySegment cif = scope.allocate(NativeConstants.SIZEOF_CIF);
|
||||
checkStatus(ffi_prep_cif(cif.address(), abi.value(), numArgs, returnType.address(), paramTypes.address()));
|
||||
return cif;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an upcallStub-style closure. This method wraps the {@code ffi_closure_alloc}
|
||||
* and {@code ffi_prep_closure_loc} functions.
|
||||
* <p>
|
||||
* The closure will end up calling into {@link #doUpcall(long, long, MethodHandle)}
|
||||
* <p>
|
||||
* The target method handle should have the type {@code (MemorySegment, MemorySegment) -> void}. The first
|
||||
* argument is a pointer to the buffer into which the native return value should be written. The second argument
|
||||
* is a pointer to an array of pointers, which each point to a native argument value.
|
||||
*
|
||||
* @param cif a pointer to a {@code ffi_cif} struct
|
||||
* @param target a method handle that points to the target function
|
||||
* @param arena the scope to which to attach the created upcall stub
|
||||
* @return the created upcall stub
|
||||
*
|
||||
* @throws IllegalStateException if the call to {@code ffi_prep_closure_loc} returns a non-zero status code
|
||||
* @throws IllegalArgumentException if {@code target} does not have the right type
|
||||
*/
|
||||
static MemorySegment createClosure(MemorySegment cif, MethodHandle target, Arena arena)
|
||||
throws IllegalStateException, IllegalArgumentException {
|
||||
if (target.type() != UPCALL_TARGET_TYPE) {
|
||||
throw new IllegalArgumentException("Target handle has wrong type: " + target.type() + " != " + UPCALL_TARGET_TYPE);
|
||||
}
|
||||
|
||||
long[] ptrs = new long[3];
|
||||
checkStatus(createClosure(cif.address(), target, ptrs));
|
||||
long closurePtr = ptrs[0];
|
||||
long execPtr = ptrs[1];
|
||||
long globalTarget = ptrs[2];
|
||||
|
||||
return MemorySegment.ofAddress(execPtr).reinterpret(arena, unused -> freeClosure(closurePtr, globalTarget));
|
||||
}
|
||||
|
||||
// the target function for a closure call
|
||||
private static void doUpcall(long retPtr, long argPtrs, MethodHandle target) {
|
||||
try {
|
||||
target.invokeExact(MemorySegment.ofAddress(retPtr), MemorySegment.ofAddress(argPtrs));
|
||||
} catch (Throwable t) {
|
||||
SharedUtils.handleUncaughtException(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for {@code ffi_get_struct_offsets}
|
||||
*
|
||||
* @param structType a pointer to an {@code ffi_type} representing a struct
|
||||
* @param offsetsOut a pointer to an array of {@code size_t}, with one element for each element of the struct.
|
||||
* This is an 'out' parameter that will be filled in by this call
|
||||
* @param abi the abi to be used
|
||||
*
|
||||
* @throws IllegalStateException if the call to {@code ffi_get_struct_offsets} returns a non-zero status code
|
||||
*/
|
||||
static void getStructOffsets(MemorySegment structType, MemorySegment offsetsOut, FFIABI abi)
|
||||
throws IllegalStateException {
|
||||
checkStatus(ffi_get_struct_offsets(abi.value(), structType.address(), offsetsOut.address()));
|
||||
}
|
||||
|
||||
private static void checkStatus(int code) {
|
||||
FFIStatus status = FFIStatus.of(code);
|
||||
if (status != FFIStatus.FFI_OK) {
|
||||
throw new IllegalStateException("libffi call failed with status: " + status);
|
||||
}
|
||||
}
|
||||
|
||||
private static native void init();
|
||||
|
||||
private static native long sizeofCif();
|
||||
|
||||
private static native int createClosure(long cif, Object userData, long[] ptrs);
|
||||
private static native void freeClosure(long closureAddress, long globalTarget);
|
||||
private static native void doDowncall(long cif, long fn, long rvalue, long avalues, long capturedState, int capturedStateMask);
|
||||
|
||||
private static native int ffi_prep_cif(long cif, int abi, int nargs, long rtype, long atypes);
|
||||
private static native int ffi_get_struct_offsets(int abi, long type, long offsets);
|
||||
|
||||
private static native int ffi_default_abi();
|
||||
private static native short ffi_type_struct();
|
||||
|
||||
private static native long ffi_type_void();
|
||||
private static native long ffi_type_uint8();
|
||||
private static native long ffi_type_sint8();
|
||||
private static native long ffi_type_uint16();
|
||||
private static native long ffi_type_sint16();
|
||||
private static native long ffi_type_uint32();
|
||||
private static native long ffi_type_sint32();
|
||||
private static native long ffi_type_uint64();
|
||||
private static native long ffi_type_sint64();
|
||||
private static native long ffi_type_float();
|
||||
private static native long ffi_type_double();
|
||||
private static native long ffi_type_pointer();
|
||||
|
||||
// put these in a separate class to avoid an UnsatisfiedLinkError
|
||||
// when LibFallback is initialized but the library is not present
|
||||
private static final class NativeConstants {
|
||||
private NativeConstants() {}
|
||||
|
||||
static final int DEFAULT_ABI = ffi_default_abi();
|
||||
|
||||
static final MemorySegment UINT8_TYPE = MemorySegment.ofAddress(ffi_type_uint8());
|
||||
static final MemorySegment SINT8_TYPE = MemorySegment.ofAddress(ffi_type_sint8());
|
||||
static final MemorySegment UINT16_TYPE = MemorySegment.ofAddress(ffi_type_uint16());
|
||||
static final MemorySegment SINT16_TYPE = MemorySegment.ofAddress(ffi_type_sint16());
|
||||
static final MemorySegment SINT32_TYPE = MemorySegment.ofAddress(ffi_type_sint32());
|
||||
static final MemorySegment SINT64_TYPE = MemorySegment.ofAddress(ffi_type_sint64());
|
||||
static final MemorySegment FLOAT_TYPE = MemorySegment.ofAddress(ffi_type_float());
|
||||
static final MemorySegment DOUBLE_TYPE = MemorySegment.ofAddress(ffi_type_double());
|
||||
static final MemorySegment POINTER_TYPE = MemorySegment.ofAddress(ffi_type_pointer());
|
||||
|
||||
static final MemorySegment VOID_TYPE = MemorySegment.ofAddress(ffi_type_void());
|
||||
static final short STRUCT_TAG = ffi_type_struct();
|
||||
static final long SIZEOF_CIF = sizeofCif();
|
||||
}
|
||||
}
|
||||
@ -32,7 +32,7 @@ import jdk.internal.foreign.abi.StubLocations;
|
||||
import jdk.internal.foreign.abi.VMStorage;
|
||||
import jdk.internal.foreign.abi.riscv64.linux.TypeClass;
|
||||
|
||||
public class RISCV64Architecture implements Architecture {
|
||||
public final class RISCV64Architecture implements Architecture {
|
||||
public static final Architecture INSTANCE = new RISCV64Architecture();
|
||||
|
||||
private static final short REG64_MASK = 0b0000_0000_0000_0001;
|
||||
@ -41,6 +41,9 @@ public class RISCV64Architecture implements Architecture {
|
||||
private static final int INTEGER_REG_SIZE = 8; // bytes
|
||||
private static final int FLOAT_REG_SIZE = 8;
|
||||
|
||||
// Suppresses default constructor, ensuring non-instantiability.
|
||||
private RISCV64Architecture() {}
|
||||
|
||||
@Override
|
||||
public boolean isStackType(int cls) {
|
||||
return cls == StorageType.STACK;
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
|
||||
package jdk.internal.foreign.abi.riscv64.linux;
|
||||
|
||||
import java.lang.foreign.AddressLayout;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
@ -42,7 +43,7 @@ import jdk.internal.foreign.abi.SharedUtils;
|
||||
import jdk.internal.foreign.abi.VMStorage;
|
||||
import jdk.internal.foreign.Utils;
|
||||
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.List;
|
||||
@ -52,7 +53,6 @@ import java.util.Optional;
|
||||
import static jdk.internal.foreign.abi.riscv64.linux.TypeClass.*;
|
||||
import static jdk.internal.foreign.abi.riscv64.RISCV64Architecture.*;
|
||||
import static jdk.internal.foreign.abi.riscv64.RISCV64Architecture.Regs.*;
|
||||
import static jdk.internal.foreign.PlatformLayouts.*;
|
||||
|
||||
/**
|
||||
* For the RISCV64 C ABI specifically, this class uses CallingSequenceBuilder
|
||||
@ -91,7 +91,7 @@ public class LinuxRISCV64CallArranger {
|
||||
boolean returnInMemory = isInMemoryReturn(cDesc.returnLayout());
|
||||
if (returnInMemory) {
|
||||
Class<?> carrier = MemorySegment.class;
|
||||
MemoryLayout layout = RISCV64.C_POINTER;
|
||||
MemoryLayout layout = SharedUtils.C_POINTER;
|
||||
csb.addArgumentBindings(carrier, layout, argCalc.getBindings(carrier, layout, false));
|
||||
} else if (cDesc.returnLayout().isPresent()) {
|
||||
Class<?> carrier = mt.returnType();
|
||||
@ -121,8 +121,8 @@ public class LinuxRISCV64CallArranger {
|
||||
return handle;
|
||||
}
|
||||
|
||||
public static UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc) {
|
||||
Bindings bindings = getBindings(mt, cDesc, true);
|
||||
public static UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc, LinkerOptions options) {
|
||||
Bindings bindings = getBindings(mt, cDesc, true, options);
|
||||
final boolean dropReturn = true; /* drop return, since we don't have bindings for it */
|
||||
return SharedUtils.arrangeUpcallHelper(mt, bindings.isInMemoryReturn, dropReturn, CLinux,
|
||||
bindings.callingSequence);
|
||||
@ -391,9 +391,10 @@ public class LinuxRISCV64CallArranger {
|
||||
bindings.vmLoad(storage, carrier);
|
||||
}
|
||||
case POINTER -> {
|
||||
AddressLayout addressLayout = (AddressLayout) layout;
|
||||
VMStorage storage = storageCalculator.getStorage(StorageType.INTEGER);
|
||||
bindings.vmLoad(storage, long.class)
|
||||
.boxAddressRaw(Utils.pointeeSize(layout));
|
||||
.boxAddressRaw(Utils.pointeeByteSize(addressLayout), Utils.pointeeByteAlign(addressLayout));
|
||||
}
|
||||
case STRUCT_REGISTER_X -> {
|
||||
assert carrier == MemorySegment.class;
|
||||
|
||||
@ -29,13 +29,9 @@ package jdk.internal.foreign.abi.riscv64.linux;
|
||||
import jdk.internal.foreign.abi.AbstractLinker;
|
||||
import jdk.internal.foreign.abi.LinkerOptions;
|
||||
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.VaList;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class LinuxRISCV64Linker extends AbstractLinker {
|
||||
|
||||
@ -57,21 +53,7 @@ public final class LinuxRISCV64Linker extends AbstractLinker {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function) {
|
||||
return LinuxRISCV64CallArranger.arrangeUpcall(targetType, function);
|
||||
}
|
||||
|
||||
public static VaList newVaList(Consumer<VaList.Builder> actions, SegmentScope scope) {
|
||||
LinuxRISCV64VaList.Builder builder = LinuxRISCV64VaList.builder(scope);
|
||||
actions.accept(builder);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static VaList newVaListOfAddress(long address, SegmentScope scope) {
|
||||
return LinuxRISCV64VaList.ofAddress(address, scope);
|
||||
}
|
||||
|
||||
public static VaList emptyVaList() {
|
||||
return LinuxRISCV64VaList.empty();
|
||||
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) {
|
||||
return LinuxRISCV64CallArranger.arrangeUpcall(targetType, function, options);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,302 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, Institute of Software, Chinese Academy of Sciences.
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
package jdk.internal.foreign.abi.riscv64.linux;
|
||||
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.SegmentAllocator;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import java.lang.foreign.VaList;
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
import jdk.internal.foreign.MemorySessionImpl;
|
||||
import jdk.internal.foreign.Utils;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.lang.foreign.ValueLayout.ADDRESS;
|
||||
import static jdk.internal.foreign.abi.SharedUtils.SimpleVaArg;
|
||||
import static jdk.internal.foreign.abi.SharedUtils.THROWING_ALLOCATOR;
|
||||
|
||||
/**
|
||||
* Standard va_list implementation as defined by RISC-V ABI document and used on Linux.
|
||||
* In the base integer calling convention, variadic arguments are passed in the same
|
||||
* manner as named arguments, with one exception. Variadic arguments with 2 * XLEN-bit
|
||||
* alignment and size at most 2 * XLEN bits are passed in an aligned register pair
|
||||
* (i.e., the first register in the pair is even-numbered), or on the stack by value
|
||||
* if none is available. After a variadic argument has been passed on the stack, all
|
||||
* future arguments will also be passed on the stack (i.e. the last argument register
|
||||
* may be left unused due to the aligned register pair rule).
|
||||
*/
|
||||
|
||||
public non-sealed class LinuxRISCV64VaList implements VaList {
|
||||
// The va_list type is void* on RISCV64.
|
||||
// See https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc#cc-type-representations
|
||||
|
||||
private final MemorySegment segment;
|
||||
private long offset;
|
||||
|
||||
private static final long STACK_SLOT_SIZE = 8;
|
||||
private static final VaList EMPTY
|
||||
= new SharedUtils.EmptyVaList(MemorySegment.NULL);
|
||||
|
||||
public static VaList empty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
public LinuxRISCV64VaList(MemorySegment segment, long offset) {
|
||||
this.segment = segment;
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
private static LinuxRISCV64VaList readFromAddress(long address, SegmentScope scope) {
|
||||
MemorySegment segment = MemorySegment.ofAddress(address, Long.MAX_VALUE, scope); // size unknown
|
||||
return new LinuxRISCV64VaList(segment, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextVarg(ValueLayout.OfInt layout) {
|
||||
return (int) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextVarg(ValueLayout.OfLong layout) {
|
||||
return (long) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextVarg(ValueLayout.OfDouble layout) {
|
||||
return (double) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(ValueLayout.OfAddress layout) {
|
||||
return (MemorySegment) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(allocator);
|
||||
return (MemorySegment) read(layout, allocator);
|
||||
}
|
||||
|
||||
private Object read(MemoryLayout layout) {
|
||||
return read(layout, THROWING_ALLOCATOR);
|
||||
}
|
||||
|
||||
private Object read(MemoryLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(layout);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
preAlignStack(layout);
|
||||
|
||||
return switch (typeClass) {
|
||||
case INTEGER, FLOAT, POINTER -> {
|
||||
checkStackElement(layout);
|
||||
VarHandle reader = layout.varHandle();
|
||||
MemorySegment slice = segment.asSlice(offset, layout.byteSize());
|
||||
Object res = reader.get(slice);
|
||||
postAlignStack(layout);
|
||||
yield res;
|
||||
}
|
||||
case STRUCT_REGISTER_X, STRUCT_REGISTER_F, STRUCT_REGISTER_XF -> {
|
||||
checkStackElement(layout);
|
||||
// Struct is passed indirectly via a pointer in an integer register.
|
||||
MemorySegment slice = segment.asSlice(offset, layout.byteSize());
|
||||
MemorySegment seg = allocator.allocate(layout);
|
||||
seg.copyFrom(slice);
|
||||
postAlignStack(layout);
|
||||
yield seg;
|
||||
}
|
||||
case STRUCT_REFERENCE -> {
|
||||
checkStackElement(ADDRESS);
|
||||
VarHandle addrReader = ADDRESS.varHandle();
|
||||
MemorySegment slice = segment.asSlice(offset, ADDRESS.byteSize());
|
||||
MemorySegment addr = (MemorySegment) addrReader.get(slice);
|
||||
MemorySegment seg = allocator.allocate(layout);
|
||||
seg.copyFrom(MemorySegment.ofAddress(addr.address(), layout.byteSize(), segment.scope()));
|
||||
postAlignStack(ADDRESS);
|
||||
yield seg;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void checkStackElement(MemoryLayout layout) {
|
||||
if (Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE) > segment.byteSize()) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
private void preAlignStack(MemoryLayout layout) {
|
||||
if (layout.byteSize() <= 16 && layout.byteAlignment() == 16) {
|
||||
offset = Utils.alignUp(offset, 16);
|
||||
} else {
|
||||
offset = Utils.alignUp(offset, STACK_SLOT_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
private void postAlignStack(MemoryLayout layout) {
|
||||
if (layout.byteSize() <= 16 && layout.byteAlignment() == 16) {
|
||||
offset += 16;
|
||||
} else {
|
||||
offset += Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(MemoryLayout... layouts) {
|
||||
Objects.requireNonNull(layouts);
|
||||
((MemorySessionImpl) segment.scope()).checkValidState();
|
||||
for (MemoryLayout layout : layouts) {
|
||||
Objects.requireNonNull(layout);
|
||||
preAlignStack(layout);
|
||||
postAlignStack(layout);
|
||||
}
|
||||
}
|
||||
|
||||
static LinuxRISCV64VaList.Builder builder(SegmentScope scope) {
|
||||
return new LinuxRISCV64VaList.Builder(scope);
|
||||
}
|
||||
|
||||
public static VaList ofAddress(long address, SegmentScope scope) {
|
||||
return readFromAddress(address, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaList copy() {
|
||||
MemorySessionImpl sessionImpl = (MemorySessionImpl) segment.scope();
|
||||
sessionImpl.checkValidState();
|
||||
return new LinuxRISCV64VaList(segment, offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment segment() {
|
||||
// make sure that returned segment cannot be accessed
|
||||
return segment.asSlice(0, 0);
|
||||
}
|
||||
|
||||
public long address() {
|
||||
return segment.address() + offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LinuxRISCV64VaList{" + "seg: " + address() + ", " + "offset: " + offset + '}';
|
||||
}
|
||||
|
||||
public static non-sealed class Builder implements VaList.Builder {
|
||||
|
||||
private final SegmentScope scope;
|
||||
private final List<SimpleVaArg> stackArgs = new ArrayList<>();
|
||||
|
||||
Builder(SegmentScope scope) {
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfInt layout, int value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfLong layout, long value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(GroupLayout layout, MemorySegment value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
private Builder arg(MemoryLayout layout, Object value) {
|
||||
Objects.requireNonNull(layout);
|
||||
Objects.requireNonNull(value);
|
||||
stackArgs.add(new SimpleVaArg(layout, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
boolean isEmpty() {
|
||||
return stackArgs.isEmpty();
|
||||
}
|
||||
|
||||
public VaList build() {
|
||||
if (isEmpty()) {
|
||||
return EMPTY;
|
||||
}
|
||||
long stackArgsSize = 0;
|
||||
for (SimpleVaArg arg : stackArgs) {
|
||||
MemoryLayout layout = arg.layout;
|
||||
long elementSize = TypeClass.classifyLayout(layout) == TypeClass.STRUCT_REFERENCE ?
|
||||
ADDRESS.byteSize() : layout.byteSize();
|
||||
// arguments with 2 * XLEN-bit alignment and size at most 2 * XLEN bits
|
||||
// are saved on memory aligned with 2 * XLEN (XLEN=64 for RISCV64).
|
||||
if (layout.byteSize() <= 16 && layout.byteAlignment() == 16) {
|
||||
stackArgsSize = Utils.alignUp(stackArgsSize, 16);
|
||||
elementSize = 16;
|
||||
}
|
||||
stackArgsSize += Utils.alignUp(elementSize, STACK_SLOT_SIZE);
|
||||
}
|
||||
MemorySegment argsSegment = MemorySegment.allocateNative(stackArgsSize, 16, scope);
|
||||
MemorySegment writeCursor = argsSegment;
|
||||
for (SimpleVaArg arg : stackArgs) {
|
||||
MemoryLayout layout;
|
||||
Object value = arg.value;
|
||||
if (TypeClass.classifyLayout(arg.layout) == TypeClass.STRUCT_REFERENCE) {
|
||||
layout = ADDRESS;
|
||||
} else {
|
||||
layout = arg.layout;
|
||||
}
|
||||
long alignedSize = Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE);
|
||||
if (layout.byteSize() <= 16 && layout.byteAlignment() == 16) {
|
||||
writeCursor = Utils.alignUp(writeCursor, 16);
|
||||
alignedSize = 16;
|
||||
}
|
||||
if (layout instanceof GroupLayout) {
|
||||
writeCursor.copyFrom((MemorySegment) value);
|
||||
} else {
|
||||
VarHandle writer = layout.varHandle();
|
||||
writer.set(writeCursor, value);
|
||||
}
|
||||
writeCursor = writeCursor.asSlice(alignedSize);
|
||||
}
|
||||
return new LinuxRISCV64VaList(argsSegment, 0L);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -31,7 +31,7 @@ import jdk.internal.foreign.abi.VMStorage;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class X86_64Architecture implements Architecture {
|
||||
public final class X86_64Architecture implements Architecture {
|
||||
public static final Architecture INSTANCE = new X86_64Architecture();
|
||||
|
||||
private static final short REG8_H_MASK = 0b0000_0000_0000_0010;
|
||||
@ -48,6 +48,9 @@ public class X86_64Architecture implements Architecture {
|
||||
private static final int VECTOR_REG_SIZE = 16; // size of XMM register
|
||||
private static final int X87_REG_SIZE = 16;
|
||||
|
||||
// Suppresses default constructor, ensuring non-instantiability.
|
||||
private X86_64Architecture() {}
|
||||
|
||||
@Override
|
||||
public boolean isStackType(int cls) {
|
||||
return cls == StorageType.STACK;
|
||||
@ -154,7 +157,9 @@ public class X86_64Architecture implements Architecture {
|
||||
new VMStorage[][] {
|
||||
outputIntRegs,
|
||||
outputVectorRegs,
|
||||
IntStream.range(0, numX87Outputs).mapToObj(X86_64Architecture::x87Storage).toArray(VMStorage[]::new)
|
||||
IntStream.range(0, numX87Outputs)
|
||||
.mapToObj(X86_64Architecture::x87Storage)
|
||||
.toArray(VMStorage[]::new)
|
||||
},
|
||||
new VMStorage[][] {
|
||||
volatileIntRegs,
|
||||
|
||||
@ -38,18 +38,18 @@ import jdk.internal.foreign.abi.UpcallLinker;
|
||||
import jdk.internal.foreign.abi.VMStorage;
|
||||
import jdk.internal.foreign.abi.x64.X86_64Architecture;
|
||||
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.AddressLayout;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static jdk.internal.foreign.PlatformLayouts.SysV;
|
||||
import static jdk.internal.foreign.abi.Binding.vmStore;
|
||||
import static jdk.internal.foreign.abi.x64.X86_64Architecture.*;
|
||||
import static jdk.internal.foreign.abi.x64.X86_64Architecture.Regs.*;
|
||||
@ -65,6 +65,11 @@ public class CallArranger {
|
||||
private static final int MAX_INTEGER_ARGUMENT_REGISTERS = 6;
|
||||
private static final int MAX_VECTOR_ARGUMENT_REGISTERS = 8;
|
||||
|
||||
/**
|
||||
* The {@code long} native type.
|
||||
*/
|
||||
public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG;
|
||||
|
||||
private static final ABIDescriptor CSysV = X86_64Architecture.abiFor(
|
||||
new VMStorage[] { rdi, rsi, rdx, rcx, r8, r9, rax },
|
||||
new VMStorage[] { xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7 },
|
||||
@ -97,7 +102,7 @@ public class CallArranger {
|
||||
boolean returnInMemory = isInMemoryReturn(cDesc.returnLayout());
|
||||
if (returnInMemory) {
|
||||
Class<?> carrier = MemorySegment.class;
|
||||
MemoryLayout layout = SysV.C_POINTER;
|
||||
MemoryLayout layout = SharedUtils.C_POINTER;
|
||||
csb.addArgumentBindings(carrier, layout, argCalc.getBindings(carrier, layout));
|
||||
} else if (cDesc.returnLayout().isPresent()) {
|
||||
Class<?> carrier = mt.returnType();
|
||||
@ -113,7 +118,7 @@ public class CallArranger {
|
||||
|
||||
if (!forUpcall) {
|
||||
//add extra binding for number of used vector registers (used for variadic calls)
|
||||
csb.addArgumentBindings(long.class, SysV.C_LONG,
|
||||
csb.addArgumentBindings(long.class, C_LONG,
|
||||
List.of(vmStore(rax, long.class)));
|
||||
}
|
||||
|
||||
@ -133,8 +138,8 @@ public class CallArranger {
|
||||
return handle;
|
||||
}
|
||||
|
||||
public static UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc) {
|
||||
Bindings bindings = getBindings(mt, cDesc, true);
|
||||
public static UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc, LinkerOptions options) {
|
||||
Bindings bindings = getBindings(mt, cDesc, true, options);
|
||||
final boolean dropReturn = true; /* drop return, since we don't have bindings for it */
|
||||
return SharedUtils.arrangeUpcallHelper(mt, bindings.isInMemoryReturn, dropReturn, CSysV,
|
||||
bindings.callingSequence);
|
||||
@ -314,9 +319,10 @@ public class CallArranger {
|
||||
}
|
||||
}
|
||||
case POINTER -> {
|
||||
AddressLayout addressLayout = (AddressLayout) layout;
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER);
|
||||
bindings.vmLoad(storage, long.class)
|
||||
.boxAddressRaw(Utils.pointeeSize(layout));
|
||||
.boxAddressRaw(Utils.pointeeByteSize(addressLayout), Utils.pointeeByteAlign(addressLayout));
|
||||
}
|
||||
case INTEGER -> {
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER);
|
||||
|
||||
@ -1,479 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.internal.foreign.abi.x64.sysv;
|
||||
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.SegmentAllocator;
|
||||
import java.lang.foreign.VaList;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
|
||||
import jdk.internal.foreign.MemorySessionImpl;
|
||||
import jdk.internal.foreign.Utils;
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static jdk.internal.foreign.PlatformLayouts.SysV;
|
||||
|
||||
import static java.lang.foreign.MemoryLayout.PathElement.groupElement;
|
||||
import static jdk.internal.foreign.abi.SharedUtils.SimpleVaArg;
|
||||
import static jdk.internal.foreign.abi.SharedUtils.THROWING_ALLOCATOR;
|
||||
|
||||
// See https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf "3.5.7 Variable Argument Lists"
|
||||
public non-sealed class SysVVaList implements VaList {
|
||||
|
||||
// struct typedef __va_list_tag __va_list_tag {
|
||||
// unsigned int gp_offset; /* 0 4 */
|
||||
// unsigned int fp_offset; /* 4 4 */
|
||||
// void * overflow_arg_area; /* 8 8 */
|
||||
// void * reg_save_area; /* 16 8 */
|
||||
//
|
||||
// /* size: 24, cachelines: 1, members: 4 */
|
||||
// /* last cacheline: 24 bytes */
|
||||
// };
|
||||
static final GroupLayout LAYOUT = MemoryLayout.structLayout(
|
||||
SysV.C_INT.withName("gp_offset"),
|
||||
SysV.C_INT.withName("fp_offset"),
|
||||
SysV.C_POINTER.withName("overflow_arg_area"),
|
||||
SysV.C_POINTER.withName("reg_save_area")
|
||||
).withName("__va_list_tag");
|
||||
|
||||
private static final long STACK_SLOT_SIZE = 8;
|
||||
|
||||
private static final MemoryLayout GP_REG = MemoryLayout.paddingLayout(64).withBitAlignment(64);
|
||||
private static final MemoryLayout FP_REG = MemoryLayout.paddingLayout(128).withBitAlignment(128);
|
||||
|
||||
private static final GroupLayout LAYOUT_REG_SAVE_AREA = MemoryLayout.structLayout(
|
||||
GP_REG.withName("%rdi"),
|
||||
GP_REG.withName("%rsi"),
|
||||
GP_REG.withName("%rdx"),
|
||||
GP_REG.withName("%rcx"),
|
||||
GP_REG.withName("%r8"),
|
||||
GP_REG.withName("%r9"),
|
||||
FP_REG.withName("%xmm0"),
|
||||
FP_REG.withName("%xmm1"),
|
||||
FP_REG.withName("%xmm2"),
|
||||
FP_REG.withName("%xmm3"),
|
||||
FP_REG.withName("%xmm4"),
|
||||
FP_REG.withName("%xmm5"),
|
||||
FP_REG.withName("%xmm6"),
|
||||
FP_REG.withName("%xmm7")
|
||||
// specification and implementation differ as to whether the following are part of a reg save area
|
||||
// Let's go with the implementation, since then it actually works :)
|
||||
// FP_REG.withName("%xmm8"),
|
||||
// FP_REG.withName("%xmm9"),
|
||||
// FP_REG.withName("%xmm10"),
|
||||
// FP_REG.withName("%xmm11"),
|
||||
// FP_REG.withName("%xmm12"),
|
||||
// FP_REG.withName("%xmm13"),
|
||||
// FP_REG.withName("%xmm14"),
|
||||
// FP_REG.withName("%xmm15")
|
||||
);
|
||||
|
||||
private static final long FP_OFFSET = LAYOUT_REG_SAVE_AREA.byteOffset(groupElement("%xmm0"));
|
||||
|
||||
private static final int GP_SLOT_SIZE = (int) GP_REG.byteSize();
|
||||
private static final int FP_SLOT_SIZE = (int) FP_REG.byteSize();
|
||||
|
||||
private static final int MAX_GP_OFFSET = (int) FP_OFFSET; // 6 regs used
|
||||
private static final int MAX_FP_OFFSET = (int) LAYOUT_REG_SAVE_AREA.byteSize(); // 8 16 byte regs
|
||||
|
||||
private static final VarHandle VH_fp_offset = LAYOUT.varHandle(groupElement("fp_offset"));
|
||||
private static final VarHandle VH_gp_offset = LAYOUT.varHandle(groupElement("gp_offset"));
|
||||
private static final VarHandle VH_overflow_arg_area = LAYOUT.varHandle(groupElement("overflow_arg_area"));
|
||||
private static final VarHandle VH_reg_save_area = LAYOUT.varHandle(groupElement("reg_save_area"));
|
||||
|
||||
private static final VaList EMPTY = new SharedUtils.EmptyVaList(emptyListAddress());
|
||||
|
||||
private final MemorySegment segment;
|
||||
private MemorySegment overflowArgArea;
|
||||
private final MemorySegment regSaveArea;
|
||||
private final long gpLimit;
|
||||
private final long fpLimit;
|
||||
|
||||
private SysVVaList(MemorySegment segment,
|
||||
MemorySegment overflowArgArea,
|
||||
MemorySegment regSaveArea, long gpLimit, long fpLimit) {
|
||||
this.segment = segment;
|
||||
this.overflowArgArea = overflowArgArea;
|
||||
this.regSaveArea = regSaveArea;
|
||||
this.gpLimit = gpLimit;
|
||||
this.fpLimit = fpLimit;
|
||||
}
|
||||
|
||||
private static SysVVaList readFromAddress(long address, SegmentScope scope) {
|
||||
MemorySegment segment = MemorySegment.ofAddress(address, LAYOUT.byteSize(), scope);
|
||||
MemorySegment regSaveArea = getRegSaveArea(segment);
|
||||
MemorySegment overflowArgArea = getArgOverflowArea(segment);
|
||||
return new SysVVaList(segment, overflowArgArea, regSaveArea, MAX_GP_OFFSET, MAX_FP_OFFSET);
|
||||
}
|
||||
|
||||
private static MemorySegment emptyListAddress() {
|
||||
MemorySegment base = MemorySegment.allocateNative(LAYOUT, SegmentScope.global());
|
||||
VH_gp_offset.set(base, MAX_GP_OFFSET);
|
||||
VH_fp_offset.set(base, MAX_FP_OFFSET);
|
||||
VH_overflow_arg_area.set(base, MemorySegment.NULL);
|
||||
VH_reg_save_area.set(base, MemorySegment.NULL);
|
||||
return base.asSlice(0, 0);
|
||||
}
|
||||
|
||||
public static VaList empty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
private int currentGPOffset() {
|
||||
return (int) VH_gp_offset.get(segment);
|
||||
}
|
||||
|
||||
private void currentGPOffset(int i) {
|
||||
VH_gp_offset.set(segment, i);
|
||||
}
|
||||
|
||||
private int currentFPOffset() {
|
||||
return (int) VH_fp_offset.get(segment);
|
||||
}
|
||||
|
||||
private void currentFPOffset(int i) {
|
||||
VH_fp_offset.set(segment, i);
|
||||
}
|
||||
|
||||
private static MemorySegment getRegSaveArea(MemorySegment segment) {
|
||||
return ((MemorySegment)VH_reg_save_area.get(segment))
|
||||
.asSlice(0, LAYOUT_REG_SAVE_AREA.byteSize());
|
||||
}
|
||||
|
||||
private static MemorySegment getArgOverflowArea(MemorySegment segment) {
|
||||
return (MemorySegment)VH_overflow_arg_area.get(segment); // size unknown
|
||||
}
|
||||
|
||||
private long preAlignOffset(MemoryLayout layout) {
|
||||
long alignmentOffset = 0;
|
||||
if (layout.byteAlignment() > STACK_SLOT_SIZE) {
|
||||
long addr = overflowArgArea.address();
|
||||
alignmentOffset = Utils.alignUp(addr, 16) - addr;
|
||||
}
|
||||
return alignmentOffset;
|
||||
}
|
||||
|
||||
private void setOverflowArgArea(MemorySegment newSegment) {
|
||||
overflowArgArea = newSegment;
|
||||
VH_overflow_arg_area.set(segment, overflowArgArea);
|
||||
}
|
||||
|
||||
private void preAlignStack(MemoryLayout layout) {
|
||||
setOverflowArgArea(overflowArgArea.asSlice(preAlignOffset(layout)));
|
||||
}
|
||||
|
||||
private void postAlignStack(MemoryLayout layout) {
|
||||
setOverflowArgArea(overflowArgArea.asSlice(Utils.alignUp(layout.byteSize(), STACK_SLOT_SIZE)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextVarg(ValueLayout.OfInt layout) {
|
||||
return (int) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextVarg(ValueLayout.OfLong layout) {
|
||||
return (long) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextVarg(ValueLayout.OfDouble layout) {
|
||||
return (double) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(ValueLayout.OfAddress layout) {
|
||||
return (MemorySegment) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(allocator);
|
||||
return (MemorySegment) read(layout, allocator);
|
||||
}
|
||||
|
||||
private Object read(MemoryLayout layout) {
|
||||
return read(layout, THROWING_ALLOCATOR);
|
||||
}
|
||||
|
||||
private Object read(MemoryLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(layout);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass)
|
||||
|| typeClass.inMemory()) {
|
||||
checkStackElement(layout);
|
||||
preAlignStack(layout);
|
||||
return switch (typeClass.kind()) {
|
||||
case STRUCT -> {
|
||||
MemorySegment slice = overflowArgArea.asSlice(0, layout.byteSize());
|
||||
MemorySegment seg = allocator.allocate(layout);
|
||||
seg.copyFrom(slice);
|
||||
postAlignStack(layout);
|
||||
yield seg;
|
||||
}
|
||||
case POINTER, INTEGER, FLOAT -> {
|
||||
VarHandle reader = layout.varHandle();
|
||||
MemorySegment slice = overflowArgArea.asSlice(0, layout.byteSize());
|
||||
Object res = reader.get(slice);
|
||||
postAlignStack(layout);
|
||||
yield res;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
checkRegSaveAreaElement(layout, typeClass);
|
||||
return switch (typeClass.kind()) {
|
||||
case STRUCT -> {
|
||||
MemorySegment value = allocator.allocate(layout);
|
||||
int classIdx = 0;
|
||||
long offset = 0;
|
||||
while (offset < layout.byteSize()) {
|
||||
final long copy = Math.min(layout.byteSize() - offset, 8);
|
||||
boolean isSSE = typeClass.classes.get(classIdx++) == ArgumentClassImpl.SSE;
|
||||
if (isSSE) {
|
||||
MemorySegment.copy(regSaveArea, currentFPOffset(), value, offset, copy);
|
||||
currentFPOffset(currentFPOffset() + FP_SLOT_SIZE);
|
||||
} else {
|
||||
MemorySegment.copy(regSaveArea, currentGPOffset(), value, offset, copy);
|
||||
currentGPOffset(currentGPOffset() + GP_SLOT_SIZE);
|
||||
}
|
||||
offset += copy;
|
||||
}
|
||||
yield value;
|
||||
}
|
||||
case POINTER, INTEGER -> {
|
||||
VarHandle reader = layout.varHandle();
|
||||
Object res = reader.get(regSaveArea.asSlice(currentGPOffset()));
|
||||
currentGPOffset(currentGPOffset() + GP_SLOT_SIZE);
|
||||
yield res;
|
||||
}
|
||||
case FLOAT -> {
|
||||
VarHandle reader = layout.varHandle();
|
||||
Object res = reader.get(regSaveArea.asSlice(currentFPOffset()));
|
||||
currentFPOffset(currentFPOffset() + FP_SLOT_SIZE);
|
||||
yield res;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void checkRegSaveAreaElement(MemoryLayout layout, TypeClass typeClass) {
|
||||
long gpSize = typeClass.nIntegerRegs() * GP_SLOT_SIZE;
|
||||
long fpSize = typeClass.nVectorRegs() * FP_SLOT_SIZE;
|
||||
if (currentGPOffset() + gpSize > gpLimit
|
||||
|| currentFPOffset() + fpSize > fpLimit) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkStackElement(MemoryLayout layout) {
|
||||
long offset = preAlignOffset(layout);
|
||||
if (offset + layout.byteSize() > overflowArgArea.byteSize()) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(MemoryLayout... layouts) {
|
||||
Objects.requireNonNull(layouts);
|
||||
((MemorySessionImpl) segment.scope()).checkValidState();
|
||||
for (MemoryLayout layout : layouts) {
|
||||
Objects.requireNonNull(layout);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
if (isRegOverflow(currentGPOffset(), currentFPOffset(), typeClass)) {
|
||||
checkStackElement(layout);
|
||||
preAlignStack(layout);
|
||||
postAlignStack(layout);
|
||||
} else {
|
||||
checkRegSaveAreaElement(layout, typeClass);
|
||||
currentGPOffset(currentGPOffset() + (((int) typeClass.nIntegerRegs()) * GP_SLOT_SIZE));
|
||||
currentFPOffset(currentFPOffset() + (((int) typeClass.nVectorRegs()) * FP_SLOT_SIZE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static SysVVaList.Builder builder(SegmentScope scope) {
|
||||
return new SysVVaList.Builder(scope);
|
||||
}
|
||||
|
||||
public static VaList ofAddress(long address, SegmentScope scope) {
|
||||
return readFromAddress(address, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaList copy() {
|
||||
MemorySegment copy = MemorySegment.allocateNative(LAYOUT, segment.scope());
|
||||
copy.copyFrom(segment);
|
||||
return new SysVVaList(copy, overflowArgArea, regSaveArea, gpLimit, fpLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment segment() {
|
||||
// make sure that returned segment cannot be accessed
|
||||
return segment.asSlice(0, 0);
|
||||
}
|
||||
|
||||
private static boolean isRegOverflow(long currentGPOffset, long currentFPOffset, TypeClass typeClass) {
|
||||
return currentGPOffset > MAX_GP_OFFSET - typeClass.nIntegerRegs() * GP_SLOT_SIZE
|
||||
|| currentFPOffset > MAX_FP_OFFSET - typeClass.nVectorRegs() * FP_SLOT_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SysVVaList{"
|
||||
+ "gp_offset=" + currentGPOffset()
|
||||
+ ", fp_offset=" + currentFPOffset()
|
||||
+ ", overflow_arg_area=" + overflowArgArea
|
||||
+ ", reg_save_area=" + regSaveArea
|
||||
+ '}';
|
||||
}
|
||||
|
||||
public static non-sealed class Builder implements VaList.Builder {
|
||||
private final SegmentScope scope;
|
||||
private final MemorySegment reg_save_area;
|
||||
private long currentGPOffset = 0;
|
||||
private long currentFPOffset = FP_OFFSET;
|
||||
private final List<SimpleVaArg> stackArgs = new ArrayList<>();
|
||||
|
||||
public Builder(SegmentScope scope) {
|
||||
this.scope = scope;
|
||||
this.reg_save_area = MemorySegment.allocateNative(LAYOUT_REG_SAVE_AREA, scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfInt layout, int value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfLong layout, long value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(GroupLayout layout, MemorySegment value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
private Builder arg(MemoryLayout layout, Object value) {
|
||||
Objects.requireNonNull(layout);
|
||||
Objects.requireNonNull(value);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
if (isRegOverflow(currentGPOffset, currentFPOffset, typeClass)
|
||||
|| typeClass.inMemory()) {
|
||||
// stack it!
|
||||
stackArgs.add(new SimpleVaArg(layout, value));
|
||||
} else {
|
||||
switch (typeClass.kind()) {
|
||||
case STRUCT -> {
|
||||
MemorySegment valueSegment = (MemorySegment) value;
|
||||
int classIdx = 0;
|
||||
long offset = 0;
|
||||
while (offset < layout.byteSize()) {
|
||||
final long copy = Math.min(layout.byteSize() - offset, 8);
|
||||
boolean isSSE = typeClass.classes.get(classIdx++) == ArgumentClassImpl.SSE;
|
||||
if (isSSE) {
|
||||
MemorySegment.copy(valueSegment, offset, reg_save_area, currentFPOffset, copy);
|
||||
currentFPOffset += FP_SLOT_SIZE;
|
||||
} else {
|
||||
MemorySegment.copy(valueSegment, offset, reg_save_area, currentGPOffset, copy);
|
||||
currentGPOffset += GP_SLOT_SIZE;
|
||||
}
|
||||
offset += copy;
|
||||
}
|
||||
}
|
||||
case POINTER, INTEGER -> {
|
||||
VarHandle writer = layout.varHandle();
|
||||
writer.set(reg_save_area.asSlice(currentGPOffset), value);
|
||||
currentGPOffset += GP_SLOT_SIZE;
|
||||
}
|
||||
case FLOAT -> {
|
||||
VarHandle writer = layout.varHandle();
|
||||
writer.set(reg_save_area.asSlice(currentFPOffset), value);
|
||||
currentFPOffset += FP_SLOT_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private boolean isEmpty() {
|
||||
return currentGPOffset == 0 && currentFPOffset == FP_OFFSET && stackArgs.isEmpty();
|
||||
}
|
||||
|
||||
public VaList build() {
|
||||
if (isEmpty()) {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
MemorySegment vaListSegment = MemorySegment.allocateNative(LAYOUT, scope);
|
||||
MemorySegment stackArgsSegment;
|
||||
if (!stackArgs.isEmpty()) {
|
||||
long stackArgsSize = stackArgs.stream().reduce(0L,
|
||||
(acc, e) -> acc + Utils.alignUp(e.layout.byteSize(), STACK_SLOT_SIZE), Long::sum);
|
||||
stackArgsSegment = MemorySegment.allocateNative(stackArgsSize, 16, scope);
|
||||
MemorySegment writeCursor = stackArgsSegment;
|
||||
for (SimpleVaArg arg : stackArgs) {
|
||||
if (arg.layout.byteSize() > 8) {
|
||||
writeCursor = Utils.alignUp(writeCursor, Math.min(16, arg.layout.byteSize()));
|
||||
}
|
||||
if (arg.layout instanceof GroupLayout) {
|
||||
writeCursor.copyFrom((MemorySegment) arg.value);
|
||||
} else {
|
||||
VarHandle writer = arg.varHandle();
|
||||
writer.set(writeCursor, arg.value);
|
||||
}
|
||||
writeCursor = writeCursor.asSlice(Utils.alignUp(arg.layout.byteSize(), STACK_SLOT_SIZE));
|
||||
}
|
||||
} else {
|
||||
stackArgsSegment = MemorySegment.NULL;
|
||||
}
|
||||
|
||||
VH_fp_offset.set(vaListSegment, (int) FP_OFFSET);
|
||||
VH_overflow_arg_area.set(vaListSegment, stackArgsSegment);
|
||||
VH_reg_save_area.set(vaListSegment, reg_save_area);
|
||||
assert MemorySessionImpl.sameOwnerThread(reg_save_area.scope(), vaListSegment.scope());
|
||||
return new SysVVaList(vaListSegment, stackArgsSegment, reg_save_area, currentGPOffset, currentFPOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -28,12 +28,9 @@ package jdk.internal.foreign.abi.x64.sysv;
|
||||
import jdk.internal.foreign.abi.AbstractLinker;
|
||||
import jdk.internal.foreign.abi.LinkerOptions;
|
||||
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.VaList;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* ABI implementation based on System V ABI AMD64 supplement v.0.99.6
|
||||
@ -58,21 +55,7 @@ public final class SysVx64Linker extends AbstractLinker {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function) {
|
||||
return CallArranger.arrangeUpcall(targetType, function);
|
||||
}
|
||||
|
||||
public static VaList newVaList(Consumer<VaList.Builder> actions, SegmentScope scope) {
|
||||
SysVVaList.Builder builder = SysVVaList.builder(scope);
|
||||
actions.accept(builder);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static VaList newVaListOfAddress(long address, SegmentScope scope) {
|
||||
return SysVVaList.ofAddress(address, scope);
|
||||
}
|
||||
|
||||
public static VaList emptyVaList() {
|
||||
return SysVVaList.empty();
|
||||
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) {
|
||||
return CallArranger.arrangeUpcall(targetType, function, options);
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,17 +37,17 @@ import jdk.internal.foreign.abi.UpcallLinker;
|
||||
import jdk.internal.foreign.abi.VMStorage;
|
||||
import jdk.internal.foreign.abi.x64.X86_64Architecture;
|
||||
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.AddressLayout;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static jdk.internal.foreign.PlatformLayouts.Win64;
|
||||
import static jdk.internal.foreign.abi.x64.X86_64Architecture.*;
|
||||
import static jdk.internal.foreign.abi.x64.X86_64Architecture.Regs.*;
|
||||
|
||||
@ -104,7 +104,7 @@ public class CallArranger {
|
||||
boolean returnInMemory = isInMemoryReturn(cDesc.returnLayout());
|
||||
if (returnInMemory) {
|
||||
Class<?> carrier = MemorySegment.class;
|
||||
MemoryLayout layout = Win64.C_POINTER;
|
||||
MemoryLayout layout = SharedUtils.C_POINTER;
|
||||
csb.addArgumentBindings(carrier, layout, false);
|
||||
if (forUpcall) {
|
||||
csb.setReturnBindings(carrier, layout);
|
||||
@ -132,8 +132,8 @@ public class CallArranger {
|
||||
return handle;
|
||||
}
|
||||
|
||||
public static UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc) {
|
||||
Bindings bindings = getBindings(mt, cDesc, true);
|
||||
public static UpcallStubFactory arrangeUpcall(MethodType mt, FunctionDescriptor cDesc, LinkerOptions options) {
|
||||
Bindings bindings = getBindings(mt, cDesc, true, options);
|
||||
final boolean dropReturn = false; /* need the return value as well */
|
||||
return SharedUtils.arrangeUpcallHelper(mt, bindings.isInMemoryReturn, dropReturn, CWindows,
|
||||
bindings.callingSequence);
|
||||
@ -265,9 +265,10 @@ public class CallArranger {
|
||||
.boxAddress(layout);
|
||||
}
|
||||
case POINTER -> {
|
||||
AddressLayout addressLayout = (AddressLayout) layout;
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER);
|
||||
bindings.vmLoad(storage, long.class)
|
||||
.boxAddressRaw(Utils.pointeeSize(layout));
|
||||
.boxAddressRaw(Utils.pointeeByteSize(addressLayout), Utils.pointeeByteAlign(addressLayout));
|
||||
}
|
||||
case INTEGER -> {
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER);
|
||||
|
||||
@ -1,246 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.internal.foreign.abi.x64.windows;
|
||||
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.SegmentAllocator;
|
||||
import java.lang.foreign.VaList;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
|
||||
import jdk.internal.foreign.MemorySessionImpl;
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
import jdk.internal.foreign.abi.SharedUtils.SimpleVaArg;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static jdk.internal.foreign.PlatformLayouts.Win64.C_POINTER;
|
||||
|
||||
// see vadefs.h (VC header)
|
||||
//
|
||||
// in short
|
||||
// -> va_list is just a pointer to a buffer with 64 bit entries.
|
||||
// -> non-power-of-two-sized, or larger than 64 bit types passed by reference.
|
||||
// -> other types passed in 64 bit slots by normal function calling convention.
|
||||
//
|
||||
// X64 va_arg impl:
|
||||
//
|
||||
// typedef char* va_list;
|
||||
//
|
||||
// #define __crt_va_arg(ap, t) \
|
||||
// ((sizeof(t) > sizeof(__int64) || (sizeof(t) & (sizeof(t) - 1)) != 0) \
|
||||
// ? **(t**)((ap += sizeof(__int64)) - sizeof(__int64)) \
|
||||
// : *(t* )((ap += sizeof(__int64)) - sizeof(__int64)))
|
||||
//
|
||||
public non-sealed class WinVaList implements VaList {
|
||||
private static final long VA_SLOT_SIZE_BYTES = 8;
|
||||
private static final VarHandle VH_address = C_POINTER.varHandle();
|
||||
|
||||
private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemorySegment.NULL);
|
||||
|
||||
private MemorySegment segment;
|
||||
|
||||
private WinVaList(MemorySegment segment) {
|
||||
this.segment = segment;
|
||||
}
|
||||
|
||||
public static final VaList empty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextVarg(ValueLayout.OfInt layout) {
|
||||
return (int) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextVarg(ValueLayout.OfLong layout) {
|
||||
return (long) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextVarg(ValueLayout.OfDouble layout) {
|
||||
return (double) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(ValueLayout.OfAddress layout) {
|
||||
return (MemorySegment) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(allocator);
|
||||
return (MemorySegment) read(layout, allocator);
|
||||
}
|
||||
|
||||
private Object read(MemoryLayout layout) {
|
||||
return read(layout, SharedUtils.THROWING_ALLOCATOR);
|
||||
}
|
||||
|
||||
private Object read(MemoryLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(layout);
|
||||
Object res;
|
||||
checkElement(layout);
|
||||
if (layout instanceof GroupLayout) {
|
||||
TypeClass typeClass = TypeClass.typeClassFor(layout, false);
|
||||
res = switch (typeClass) {
|
||||
case STRUCT_REFERENCE -> {
|
||||
MemorySegment structAddr = (MemorySegment) VH_address.get(segment);
|
||||
MemorySegment struct = MemorySegment.ofAddress(structAddr.address(), layout.byteSize(), segment.scope());
|
||||
MemorySegment seg = allocator.allocate(layout);
|
||||
seg.copyFrom(struct);
|
||||
yield seg;
|
||||
}
|
||||
case STRUCT_REGISTER ->
|
||||
allocator.allocate(layout).copyFrom(segment.asSlice(0, layout.byteSize()));
|
||||
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
|
||||
};
|
||||
} else {
|
||||
VarHandle reader = layout.varHandle();
|
||||
res = reader.get(segment);
|
||||
}
|
||||
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
return res;
|
||||
}
|
||||
|
||||
private void checkElement(MemoryLayout layout) {
|
||||
if (segment.byteSize() < VA_SLOT_SIZE_BYTES) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(MemoryLayout... layouts) {
|
||||
Objects.requireNonNull(layouts);
|
||||
((MemorySessionImpl) segment.scope()).checkValidState();
|
||||
for (MemoryLayout layout : layouts) {
|
||||
Objects.requireNonNull(layout);
|
||||
checkElement(layout);
|
||||
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
}
|
||||
}
|
||||
|
||||
static WinVaList ofAddress(long address, SegmentScope scope) {
|
||||
return new WinVaList(MemorySegment.ofAddress(address, Long.MAX_VALUE, scope));
|
||||
}
|
||||
|
||||
static Builder builder(SegmentScope scope) {
|
||||
return new Builder(scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaList copy() {
|
||||
((MemorySessionImpl) segment.scope()).checkValidState();
|
||||
return new WinVaList(segment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment segment() {
|
||||
// make sure that returned segment cannot be accessed
|
||||
return segment.asSlice(0, 0);
|
||||
}
|
||||
|
||||
public static non-sealed class Builder implements VaList.Builder {
|
||||
|
||||
private final SegmentScope scope;
|
||||
private final List<SimpleVaArg> args = new ArrayList<>();
|
||||
|
||||
public Builder(SegmentScope scope) {
|
||||
((MemorySessionImpl) scope).checkValidState();
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
private Builder arg(MemoryLayout layout, Object value) {
|
||||
Objects.requireNonNull(layout);
|
||||
Objects.requireNonNull(value);
|
||||
args.add(new SimpleVaArg(layout, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfInt layout, int value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfLong layout, long value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(GroupLayout layout, MemorySegment value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
public VaList build() {
|
||||
if (args.isEmpty()) {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
MemorySegment segment = MemorySegment.allocateNative(VA_SLOT_SIZE_BYTES * args.size(), scope);
|
||||
MemorySegment cursor = segment;
|
||||
|
||||
for (SimpleVaArg arg : args) {
|
||||
if (arg.layout instanceof GroupLayout) {
|
||||
MemorySegment msArg = ((MemorySegment) arg.value);
|
||||
TypeClass typeClass = TypeClass.typeClassFor(arg.layout, false);
|
||||
switch (typeClass) {
|
||||
case STRUCT_REFERENCE -> {
|
||||
MemorySegment copy = MemorySegment.allocateNative(arg.layout, scope);
|
||||
copy.copyFrom(msArg); // by-value
|
||||
VH_address.set(cursor, copy);
|
||||
}
|
||||
case STRUCT_REGISTER ->
|
||||
cursor.copyFrom(msArg.asSlice(0, VA_SLOT_SIZE_BYTES));
|
||||
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
|
||||
}
|
||||
} else {
|
||||
VarHandle writer = arg.varHandle();
|
||||
writer.set(cursor, arg.value);
|
||||
}
|
||||
cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
}
|
||||
|
||||
return new WinVaList(segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -28,12 +28,8 @@ import jdk.internal.foreign.abi.AbstractLinker;
|
||||
import jdk.internal.foreign.abi.LinkerOptions;
|
||||
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.VaList;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* ABI implementation based on Windows ABI AMD64 supplement v.0.99.6
|
||||
@ -58,21 +54,7 @@ public final class Windowsx64Linker extends AbstractLinker {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function) {
|
||||
return CallArranger.arrangeUpcall(targetType, function);
|
||||
}
|
||||
|
||||
public static VaList newVaList(Consumer<VaList.Builder> actions, SegmentScope scope) {
|
||||
WinVaList.Builder builder = WinVaList.builder(scope);
|
||||
actions.accept(builder);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static VaList newVaListOfAddress(long address, SegmentScope scope) {
|
||||
return WinVaList.ofAddress(address, scope);
|
||||
}
|
||||
|
||||
public static VaList emptyVaList() {
|
||||
return WinVaList.empty();
|
||||
protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) {
|
||||
return CallArranger.arrangeUpcall(targetType, function, options);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 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
|
||||
@ -49,15 +49,13 @@ public sealed abstract class AbstractGroupLayout<L extends AbstractGroupLayout<L
|
||||
|
||||
private final Kind kind;
|
||||
private final List<MemoryLayout> elements;
|
||||
final long minBitAlignment;
|
||||
|
||||
AbstractGroupLayout(Kind kind, List<MemoryLayout> elements) {
|
||||
this(kind, elements, kind.alignof(elements), Optional.empty());
|
||||
}
|
||||
|
||||
AbstractGroupLayout(Kind kind, List<MemoryLayout> elements, long bitAlignment, Optional<String> name) {
|
||||
super(kind.sizeof(elements), bitAlignment, name); // Subclassing creates toctou problems here
|
||||
AbstractGroupLayout(Kind kind, List<MemoryLayout> elements, long bitSize, long bitAlignment, long minBitAlignment, Optional<String> name) {
|
||||
super(bitSize, bitAlignment, name); // Subclassing creates toctou problems here
|
||||
this.kind = kind;
|
||||
this.elements = List.copyOf(elements);
|
||||
this.minBitAlignment = minBitAlignment;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,20 +81,24 @@ public sealed abstract class AbstractGroupLayout<L extends AbstractGroupLayout<L
|
||||
.collect(Collectors.joining(kind.delimTag, "[", "]")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public L withBitAlignment(long bitAlignment) {
|
||||
if (bitAlignment < minBitAlignment) {
|
||||
throw new IllegalArgumentException("Invalid alignment constraint");
|
||||
}
|
||||
return super.withBitAlignment(bitAlignment);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public final boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!super.equals(other)) {
|
||||
return false;
|
||||
}
|
||||
return other instanceof AbstractGroupLayout<?> otherGroup &&
|
||||
kind == otherGroup.kind &&
|
||||
elements.equals(otherGroup.elements);
|
||||
return this == other ||
|
||||
other instanceof AbstractGroupLayout<?> otherGroup &&
|
||||
super.equals(other) &&
|
||||
kind == otherGroup.kind &&
|
||||
elements.equals(otherGroup.elements);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -109,7 +111,7 @@ public sealed abstract class AbstractGroupLayout<L extends AbstractGroupLayout<L
|
||||
|
||||
@Override
|
||||
public final boolean hasNaturalAlignment() {
|
||||
return bitAlignment() == kind.alignof(elements);
|
||||
return bitAlignment() == minBitAlignment;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -119,33 +121,16 @@ public sealed abstract class AbstractGroupLayout<L extends AbstractGroupLayout<L
|
||||
/**
|
||||
* A 'struct' kind.
|
||||
*/
|
||||
STRUCT("", Math::addExact),
|
||||
STRUCT(""),
|
||||
/**
|
||||
* A 'union' kind.
|
||||
*/
|
||||
UNION("|", Math::max);
|
||||
UNION("|");
|
||||
|
||||
final String delimTag;
|
||||
final LongBinaryOperator sizeOp;
|
||||
|
||||
Kind(String delimTag, LongBinaryOperator sizeOp) {
|
||||
Kind(String delimTag) {
|
||||
this.delimTag = delimTag;
|
||||
this.sizeOp = sizeOp;
|
||||
}
|
||||
|
||||
long sizeof(List<MemoryLayout> elems) {
|
||||
long size = 0;
|
||||
for (MemoryLayout elem : elems) {
|
||||
size = sizeOp.applyAsLong(size, elem.bitSize());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
long alignof(List<MemoryLayout> elems) {
|
||||
return elems.stream()
|
||||
.mapToLong(MemoryLayout::bitAlignment)
|
||||
.max() // max alignment in case we have member layouts
|
||||
.orElse(1); // or minimal alignment if no member layout is given
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 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,10 +25,6 @@
|
||||
*/
|
||||
package jdk.internal.foreign.layout;
|
||||
|
||||
import jdk.internal.foreign.Utils;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.SequenceLayout;
|
||||
@ -41,51 +37,50 @@ import java.util.Optional;
|
||||
public abstract sealed class AbstractLayout<L extends AbstractLayout<L> & MemoryLayout>
|
||||
permits AbstractGroupLayout, PaddingLayoutImpl, SequenceLayoutImpl, ValueLayouts.AbstractValueLayout {
|
||||
|
||||
private final long bitSize;
|
||||
private final long bitAlignment;
|
||||
private final long byteSize;
|
||||
private final long byteAlignment;
|
||||
private final Optional<String> name;
|
||||
@Stable
|
||||
private long byteSize;
|
||||
|
||||
AbstractLayout(long bitSize, long bitAlignment, Optional<String> name) {
|
||||
this.bitSize = bitSize;
|
||||
this.bitAlignment = bitAlignment;
|
||||
this.name = name;
|
||||
this.byteSize = MemoryLayoutUtil.requireBitSizeValid(bitSize, true) / 8;
|
||||
this.byteAlignment = requirePowerOfTwoAndGreaterOrEqualToEight(bitAlignment) / 8;
|
||||
this.name = Objects.requireNonNull(name);
|
||||
}
|
||||
|
||||
public final L withName(String name) {
|
||||
Objects.requireNonNull(name);
|
||||
return dup(bitAlignment, Optional.of(name));
|
||||
return dup(bitAlignment(), Optional.of(name));
|
||||
}
|
||||
|
||||
public final L withoutName() {
|
||||
return dup(bitAlignment(), Optional.empty());
|
||||
}
|
||||
|
||||
public final Optional<String> name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public final L withBitAlignment(long bitAlignment) {
|
||||
checkAlignment(bitAlignment);
|
||||
public L withBitAlignment(long bitAlignment) {
|
||||
return dup(bitAlignment, name);
|
||||
}
|
||||
|
||||
public final long bitAlignment() {
|
||||
return bitAlignment;
|
||||
return byteAlignment * 8;
|
||||
}
|
||||
|
||||
public final long byteAlignment() {
|
||||
return byteAlignment;
|
||||
}
|
||||
|
||||
@ForceInline
|
||||
public final long byteSize() {
|
||||
if (byteSize == 0) {
|
||||
byteSize = Utils.bitsToBytesOrThrow(bitSize(),
|
||||
() -> new UnsupportedOperationException("Cannot compute byte size; bit size is not a multiple of 8"));
|
||||
}
|
||||
return byteSize;
|
||||
}
|
||||
|
||||
public final long bitSize() {
|
||||
return bitSize;
|
||||
return byteSize * 8;
|
||||
}
|
||||
|
||||
public boolean hasNaturalAlignment() {
|
||||
return bitSize == bitAlignment;
|
||||
return byteSize == byteAlignment;
|
||||
}
|
||||
|
||||
// the following methods have to copy the same Javadoc as in MemoryLayout, or subclasses will just show
|
||||
@ -96,7 +91,7 @@ public abstract sealed class AbstractLayout<L extends AbstractLayout<L> & Memory
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name, bitSize, bitAlignment);
|
||||
return Objects.hash(name, byteSize, byteAlignment);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -118,39 +113,36 @@ public abstract sealed class AbstractLayout<L extends AbstractLayout<L> & Memory
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return other instanceof AbstractLayout<?> otherLayout &&
|
||||
name.equals(otherLayout.name) &&
|
||||
bitSize == otherLayout.bitSize &&
|
||||
bitAlignment == otherLayout.bitAlignment;
|
||||
byteSize == otherLayout.byteSize &&
|
||||
byteAlignment == otherLayout.byteAlignment;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the string representation of this layout}
|
||||
*/
|
||||
@Override
|
||||
public abstract String toString();
|
||||
|
||||
abstract L dup(long alignment, Optional<String> name);
|
||||
abstract L dup(long bitAlignment, Optional<String> name);
|
||||
|
||||
String decorateLayoutString(String s) {
|
||||
if (name().isPresent()) {
|
||||
s = String.format("%s(%s)", s, name().get());
|
||||
}
|
||||
if (!hasNaturalAlignment()) {
|
||||
s = bitAlignment + "%" + s;
|
||||
s = bitAlignment() + "%" + s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
private static void checkAlignment(long alignmentBitCount) {
|
||||
if (((alignmentBitCount & (alignmentBitCount - 1)) != 0L) || //alignment must be a power of two
|
||||
(alignmentBitCount < 8)) { //alignment must be greater than 8
|
||||
throw new IllegalArgumentException("Invalid alignment: " + alignmentBitCount);
|
||||
private static long requirePowerOfTwoAndGreaterOrEqualToEight(long value) {
|
||||
if (((value & (value - 1)) != 0L) || // value must be a power of two
|
||||
(value < 8)) { // value must be greater or equal to 8
|
||||
throw new IllegalArgumentException("Invalid alignment: " + value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 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
|
||||
@ -30,14 +30,18 @@ public final class MemoryLayoutUtil {
|
||||
private MemoryLayoutUtil() {
|
||||
}
|
||||
|
||||
public static void checkSize(long size) {
|
||||
checkSize(size, false);
|
||||
public static long requireNonNegative(long value) {
|
||||
if (value < 0) {
|
||||
throw new IllegalArgumentException("The provided value was negative: " + value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static void checkSize(long size, boolean includeZero) {
|
||||
if (size < 0 || (!includeZero && size == 0)) {
|
||||
throw new IllegalArgumentException("Invalid size for layout: " + size);
|
||||
public static long requireBitSizeValid(long bitSize, boolean allowZero) {
|
||||
if ((bitSize == 0 && !allowZero) || bitSize < 0 || bitSize % 8 != 0) {
|
||||
throw new IllegalArgumentException("Invalid bitSize: " + bitSize);
|
||||
}
|
||||
return bitSize;
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user