From 20f15352a3014042aa69f7cbfb67de0f7fdddb40 Mon Sep 17 00:00:00 2001 From: Martin Doerr Date: Wed, 24 May 2023 08:38:34 +0000 Subject: [PATCH] 8303040: linux PPC64le: Implementation of Foreign Function & Memory API (Preview) Reviewed-by: jvernee, rrich --- src/hotspot/cpu/aarch64/vmstorage_aarch64.hpp | 2 +- src/hotspot/cpu/arm/vmstorage_arm.hpp | 2 +- src/hotspot/cpu/ppc/downcallLinker_ppc.cpp | 328 ++++++++++++- src/hotspot/cpu/ppc/foreignGlobals_ppc.cpp | 226 ++++++++- src/hotspot/cpu/ppc/foreignGlobals_ppc.hpp | 26 +- src/hotspot/cpu/ppc/frame_ppc.cpp | 27 +- src/hotspot/cpu/ppc/frame_ppc.inline.hpp | 5 +- src/hotspot/cpu/ppc/methodHandles_ppc.cpp | 9 +- src/hotspot/cpu/ppc/upcallLinker_ppc.cpp | 337 ++++++++++++- src/hotspot/cpu/ppc/vmstorage_ppc.hpp | 71 ++- src/hotspot/cpu/riscv/vmstorage_riscv.hpp | 2 +- src/hotspot/cpu/s390/vmstorage_s390.hpp | 2 +- src/hotspot/cpu/x86/vmstorage_x86.hpp | 2 +- src/hotspot/cpu/zero/vmstorage_zero.hpp | 2 +- src/hotspot/share/prims/foreignGlobals.cpp | 2 +- .../classes/jdk/internal/foreign/CABI.java | 5 + .../internal/foreign/abi/AbstractLinker.java | 5 +- .../jdk/internal/foreign/abi/SharedUtils.java | 2 + .../foreign/abi/ppc64/ABIv2CallArranger.java | 35 ++ .../foreign/abi/ppc64/CallArranger.java | 464 ++++++++++++++++++ .../foreign/abi/ppc64/PPC64Architecture.java | 181 +++++++ .../internal/foreign/abi/ppc64/TypeClass.java | 136 +++++ .../abi/ppc64/linux/LinuxPPC64leLinker.java | 65 +++ .../compiler/TestLinkToNativeRBP.java | 2 +- test/jdk/java/foreign/TestHFA.java | 420 ++++++++++++++++ .../platform/PlatformLayouts.java | 55 +++ test/jdk/java/foreign/libTestHFA.c | 119 +++++ 27 files changed, 2485 insertions(+), 47 deletions(-) create mode 100644 src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/ABIv2CallArranger.java create mode 100644 src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/CallArranger.java create mode 100644 src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/PPC64Architecture.java create mode 100644 src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/TypeClass.java create mode 100644 src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/linux/LinuxPPC64leLinker.java create mode 100644 test/jdk/java/foreign/TestHFA.java create mode 100644 test/jdk/java/foreign/libTestHFA.c diff --git a/src/hotspot/cpu/aarch64/vmstorage_aarch64.hpp b/src/hotspot/cpu/aarch64/vmstorage_aarch64.hpp index 7c6d34c802f..e46519e8862 100644 --- a/src/hotspot/cpu/aarch64/vmstorage_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/vmstorage_aarch64.hpp @@ -68,7 +68,7 @@ constexpr inline VMStorage as_VMStorage(FloatRegister reg) { return VMStorage::reg_storage(StorageType::VECTOR, V128_MASK, reg->encoding()); } -inline VMStorage as_VMStorage(VMReg reg) { +inline VMStorage as_VMStorage(VMReg reg, BasicType bt) { if (reg->is_Register()) { return as_VMStorage(reg->as_Register()); } else if (reg->is_FloatRegister()) { diff --git a/src/hotspot/cpu/arm/vmstorage_arm.hpp b/src/hotspot/cpu/arm/vmstorage_arm.hpp index fda9ceac0a6..41bebd01fd7 100644 --- a/src/hotspot/cpu/arm/vmstorage_arm.hpp +++ b/src/hotspot/cpu/arm/vmstorage_arm.hpp @@ -44,7 +44,7 @@ constexpr inline StorageType VMStorage::stack_type() { return StorageType::STACK constexpr inline StorageType VMStorage::placeholder_type() { return StorageType::PLACEHOLDER; } constexpr inline StorageType VMStorage::frame_data_type() { return StorageType::FRAME_DATA; } -inline VMStorage as_VMStorage(VMReg reg) { +inline VMStorage as_VMStorage(VMReg reg, BasicType bt) { ShouldNotReachHere(); return VMStorage::invalid(); } diff --git a/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp b/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp index 0a4c2c31086..1cd1d92de99 100644 --- a/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp +++ b/src/hotspot/cpu/ppc/downcallLinker_ppc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 SAP SE. All rights reserved. + * Copyright (c) 2020, 2023 SAP SE. 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. * @@ -23,8 +23,78 @@ */ #include "precompiled.hpp" +#include "asm/macroAssembler.inline.hpp" +#include "code/codeBlob.hpp" +#include "code/codeCache.hpp" +#include "code/vmreg.inline.hpp" +#include "compiler/oopMap.hpp" +#include "logging/logStream.hpp" +#include "memory/resourceArea.hpp" #include "prims/downcallLinker.hpp" -#include "utilities/debug.hpp" +#include "runtime/globals.hpp" +#include "runtime/stubCodeGenerator.hpp" + +#define __ _masm-> + +class DowncallStubGenerator : public StubCodeGenerator { + BasicType* _signature; + int _num_args; + BasicType _ret_bt; + const ABIDescriptor& _abi; + + const GrowableArray& _input_registers; + const GrowableArray& _output_registers; + + bool _needs_return_buffer; + int _captured_state_mask; + bool _needs_transition; + + int _frame_complete; + int _frame_size_slots; + OopMapSet* _oop_maps; +public: + DowncallStubGenerator(CodeBuffer* buffer, + BasicType* signature, + int num_args, + BasicType ret_bt, + const ABIDescriptor& abi, + const GrowableArray& input_registers, + const GrowableArray& output_registers, + bool needs_return_buffer, + int captured_state_mask, + bool needs_transition) + : StubCodeGenerator(buffer, PrintMethodHandleStubs), + _signature(signature), + _num_args(num_args), + _ret_bt(ret_bt), + _abi(abi), + _input_registers(input_registers), + _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) { + } + + void generate(); + + int frame_complete() const { + return _frame_complete; + } + + int framesize() const { + return (_frame_size_slots >> (LogBytesPerWord - LogBytesPerInt)); + } + + OopMapSet* oop_maps() const { + return _oop_maps; + } +}; + +static const int native_invoker_code_base_size = 384; +static const int native_invoker_size_per_arg = 8; RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature, int num_args, @@ -35,6 +105,256 @@ RuntimeStub* DowncallLinker::make_downcall_stub(BasicType* signature, bool needs_return_buffer, int captured_state_mask, bool needs_transition) { - Unimplemented(); - return nullptr; + 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_transition); + g.generate(); + code.log_section_sizes("nep_invoker_blob"); + + RuntimeStub* stub = + RuntimeStub::new_runtime_stub("nep_invoker_blob", + &code, + g.frame_complete(), + g.framesize(), + g.oop_maps(), false); + +#ifndef PRODUCT + LogTarget(Trace, foreign, downcall) lt; + if (lt.is_enabled()) { + ResourceMark rm; + LogStream ls(lt); + stub->print_on(&ls); + } +#endif + + return stub; +} + +void DowncallStubGenerator::generate() { + Register callerSP = R2, // C/C++ uses R2 as TOC, but we can reuse it here + tmp = R11_scratch1, // same as shuffle_reg + call_target_address = R12_scratch2; // same as _abi._scratch2 (ABIv2 requires this reg!) + VMStorage shuffle_reg = _abi._scratch1; + JavaCallingConvention in_conv; + NativeCallingConvention out_conv(_input_registers); + ArgumentShuffle arg_shuffle(_signature, _num_args, _signature, _num_args, &in_conv, &out_conv, shuffle_reg); + +#ifndef PRODUCT + LogTarget(Trace, foreign, downcall) lt; + if (lt.is_enabled()) { + ResourceMark rm; + LogStream ls(lt); + arg_shuffle.print_on(&ls); + } +#endif + + // Stack frame size computation: + // We use the number of input VMStorage elements because PPC64 requires slots for all arguments + // (even if they are passed in registers), at least 8 (exception for ABIv2: see below). + // This may be a bit more than needed when single precision HFA is used (see CallArranger.java). + // (native_abi_reg_args is native_abi_minframe plus space for 8 argument register spill slots) + assert(_abi._shadow_space_bytes == frame::native_abi_minframe_size, "expected space according to ABI"); + // The Parameter Save Area needs to be at least 8 slots for ABIv1. + // ABIv2 allows omitting it if the callee's prototype indicates that all parameters can be passed in registers. + // For ABIv2, we typically only need (_input_registers.length() > 8) ? _input_registers.length() : 0, + // but this may be wrong for VarArgs. So, we currently don't optimize this. + int parameter_save_area_slots = MAX2(_input_registers.length(), 8); + int allocated_frame_size = frame::native_abi_minframe_size + parameter_save_area_slots * BytesPerWord; + + bool should_save_return_value = !_needs_return_buffer && _needs_transition; + RegSpiller out_reg_spiller(_output_registers); + int spill_offset = -1; + + if (should_save_return_value) { + spill_offset = frame::native_abi_reg_args_size; + // Spill area can be shared with additional out args (>8), + // since it is only used after the call. + int frame_size_including_spill_area = frame::native_abi_reg_args_size + out_reg_spiller.spill_size_bytes(); + if (frame_size_including_spill_area > allocated_frame_size) { + allocated_frame_size = frame_size_including_spill_area; + } + } + + StubLocations locs; + assert(as_Register(_abi._scratch2) == call_target_address, "required by ABIv2"); + locs.set(StubLocations::TARGET_ADDRESS, _abi._scratch2); + if (_needs_return_buffer) { + locs.set_frame_data(StubLocations::RETURN_BUFFER, allocated_frame_size); + allocated_frame_size += BytesPerWord; // for address spill + } + if (_captured_state_mask != 0) { + locs.set_frame_data(StubLocations::CAPTURED_STATE_BUFFER, allocated_frame_size); + allocated_frame_size += BytesPerWord; + } + + allocated_frame_size = align_up(allocated_frame_size, StackAlignmentInBytes); + _frame_size_slots = allocated_frame_size >> LogBytesPerInt; + + _oop_maps = _needs_transition ? new OopMapSet() : nullptr; + address start = __ pc(); + + __ save_LR_CR(tmp); // Save in old frame. + __ mr(callerSP, R1_SP); // preset (used to access caller frame argument slots) + __ push_frame(allocated_frame_size, tmp); + + _frame_complete = __ pc() - start; + + if (_needs_transition) { + address the_pc = __ pc(); + __ calculate_address_from_global_toc(tmp, the_pc, true, true, true, true); + __ set_last_Java_frame(R1_SP, tmp); + OopMap* map = new OopMap(_frame_size_slots, 0); + _oop_maps->add_gc_map(the_pc - start, map); + + // State transition + __ li(R0, _thread_in_native); + __ release(); + __ stw(R0, in_bytes(JavaThread::thread_state_offset()), R16_thread); + } + + __ block_comment("{ argument shuffle"); + arg_shuffle.generate(_masm, as_VMStorage(callerSP), frame::jit_out_preserve_size, frame::native_abi_minframe_size, locs); + __ block_comment("} argument shuffle"); + + __ call_c(call_target_address); + + if (_needs_return_buffer) { + // Store return values as required by BoxBindingCalculator. + __ ld(tmp, locs.data_offset(StubLocations::RETURN_BUFFER), R1_SP); + int offset = 0; + for (int i = 0; i < _output_registers.length(); i++) { + VMStorage reg = _output_registers.at(i); + if (reg.type() == StorageType::INTEGER) { + // Store in matching size (not relevant for little endian). + if (reg.segment_mask() == REG32_MASK) { + __ stw(as_Register(reg), offset, tmp); + } else { + __ std(as_Register(reg), offset, tmp); + } + offset += 8; + } else if (reg.type() == StorageType::FLOAT) { + // Java code doesn't perform float-double format conversions. Do it here. + if (reg.segment_mask() == REG32_MASK) { + __ stfs(as_FloatRegister(reg), offset, tmp); + } else { + __ stfd(as_FloatRegister(reg), offset, tmp); + } + offset += 8; + } else { + ShouldNotReachHere(); + } + } + } + + ////////////////////////////////////////////////////////////////////////////// + + if (_captured_state_mask != 0) { + __ block_comment("{ save thread local"); + + if (should_save_return_value) { + out_reg_spiller.generate_spill(_masm, spill_offset); + } + + __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, DowncallLinker::capture_state), R0); + __ ld(R3_ARG1, locs.data_offset(StubLocations::CAPTURED_STATE_BUFFER), R1_SP); + __ load_const_optimized(R4_ARG2, _captured_state_mask, R0); + __ call_c(call_target_address); + + if (should_save_return_value) { + out_reg_spiller.generate_fill(_masm, spill_offset); + } + + __ block_comment("} save thread local"); + } + + ////////////////////////////////////////////////////////////////////////////// + + Label L_after_safepoint_poll; + Label L_safepoint_poll_slow_path; + Label L_reguard; + Label L_after_reguard; + + if (_needs_transition) { + __ li(tmp, _thread_in_native_trans); + __ release(); + __ stw(tmp, in_bytes(JavaThread::thread_state_offset()), R16_thread); + if (!UseSystemMemoryBarrier) { + __ fence(); // Order state change wrt. safepoint poll. + } + + __ safepoint_poll(L_safepoint_poll_slow_path, tmp, true /* at_return */, false /* in_nmethod */); + + __ lwz(tmp, in_bytes(JavaThread::suspend_flags_offset()), R16_thread); + __ cmpwi(CCR0, tmp, 0); + __ bne(CCR0, L_safepoint_poll_slow_path); + __ bind(L_after_safepoint_poll); + + // change thread state + __ li(tmp, _thread_in_Java); + __ lwsync(); // Acquire safepoint and suspend state, release thread state. + __ stw(tmp, in_bytes(JavaThread::thread_state_offset()), R16_thread); + + __ block_comment("reguard stack check"); + __ lwz(tmp, in_bytes(JavaThread::stack_guard_state_offset()), R16_thread); + __ cmpwi(CCR0, tmp, StackOverflow::stack_guard_yellow_reserved_disabled); + __ beq(CCR0, L_reguard); + __ bind(L_after_reguard); + + __ reset_last_Java_frame(); + } + + __ pop_frame(); + __ restore_LR_CR(tmp); + __ blr(); + + ////////////////////////////////////////////////////////////////////////////// + + 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); + } + + __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, JavaThread::check_special_condition_for_native_trans), R0); + __ mr(R3_ARG1, R16_thread); + __ call_c(call_target_address); + + 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"); + + ////////////////////////////////////////////////////////////////////////////// + + __ block_comment("{ L_reguard"); + __ bind(L_reguard); + + if (should_save_return_value) { + out_reg_spiller.generate_spill(_masm, spill_offset); + } + + __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, SharedRuntime::reguard_yellow_pages), R0); + __ call_c(call_target_address); + + if (should_save_return_value) { + out_reg_spiller.generate_fill(_masm, spill_offset); + } + + __ b(L_after_reguard); + + __ block_comment("} L_reguard"); + } + + ////////////////////////////////////////////////////////////////////////////// + + __ flush(); } diff --git a/src/hotspot/cpu/ppc/foreignGlobals_ppc.cpp b/src/hotspot/cpu/ppc/foreignGlobals_ppc.cpp index cf9904cfae5..6e10aacaad1 100644 --- a/src/hotspot/cpu/ppc/foreignGlobals_ppc.cpp +++ b/src/hotspot/cpu/ppc/foreignGlobals_ppc.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, 2023, 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 @@ -23,35 +23,239 @@ */ #include "precompiled.hpp" -#include "code/vmreg.hpp" +#include "asm/macroAssembler.inline.hpp" +#include "code/vmreg.inline.hpp" +#include "runtime/jniHandles.hpp" +#include "runtime/jniHandles.inline.hpp" +#include "oops/typeArrayOop.inline.hpp" +#include "oops/oopCast.inline.hpp" #include "prims/foreignGlobals.hpp" -#include "utilities/debug.hpp" +#include "prims/foreignGlobals.inline.hpp" +#include "prims/vmstorage.hpp" +#include "utilities/formatBuffer.hpp" -class MacroAssembler; +#define __ masm-> + +bool ABIDescriptor::is_volatile_reg(Register reg) const { + return _integer_argument_registers.contains(reg) + || _integer_additional_volatile_registers.contains(reg); +} + +bool ABIDescriptor::is_volatile_reg(FloatRegister reg) const { + return _float_argument_registers.contains(reg) + || _float_additional_volatile_registers.contains(reg); +} bool ForeignGlobals::is_foreign_linker_supported() { +#ifdef ABI_ELFv2 + return true; +#else return false; +#endif } // Stubbed out, implement later const ABIDescriptor ForeignGlobals::parse_abi_descriptor(jobject jabi) { - Unimplemented(); - return {}; + oop abi_oop = JNIHandles::resolve_non_null(jabi); + ABIDescriptor abi; + + objArrayOop inputStorage = jdk_internal_foreign_abi_ABIDescriptor::inputStorage(abi_oop); + parse_register_array(inputStorage, StorageType::INTEGER, abi._integer_argument_registers, as_Register); + parse_register_array(inputStorage, StorageType::FLOAT, abi._float_argument_registers, as_FloatRegister); + + objArrayOop outputStorage = jdk_internal_foreign_abi_ABIDescriptor::outputStorage(abi_oop); + parse_register_array(outputStorage, StorageType::INTEGER, abi._integer_return_registers, as_Register); + parse_register_array(outputStorage, StorageType::FLOAT, abi._float_return_registers, as_FloatRegister); + + objArrayOop volatileStorage = jdk_internal_foreign_abi_ABIDescriptor::volatileStorage(abi_oop); + parse_register_array(volatileStorage, StorageType::INTEGER, abi._integer_additional_volatile_registers, as_Register); + parse_register_array(volatileStorage, StorageType::FLOAT, abi._float_additional_volatile_registers, as_FloatRegister); + + abi._stack_alignment_bytes = jdk_internal_foreign_abi_ABIDescriptor::stackAlignment(abi_oop); + abi._shadow_space_bytes = jdk_internal_foreign_abi_ABIDescriptor::shadowSpace(abi_oop); + + abi._scratch1 = parse_vmstorage(jdk_internal_foreign_abi_ABIDescriptor::scratch1(abi_oop)); + abi._scratch2 = parse_vmstorage(jdk_internal_foreign_abi_ABIDescriptor::scratch2(abi_oop)); + + return abi; } int RegSpiller::pd_reg_size(VMStorage reg) { - Unimplemented(); - return -1; + if (reg.type() == StorageType::INTEGER || reg.type() == StorageType::FLOAT) { + return 8; + } + return 0; // stack and BAD } void RegSpiller::pd_store_reg(MacroAssembler* masm, int offset, VMStorage reg) { - Unimplemented(); + if (reg.type() == StorageType::INTEGER) { + __ std(as_Register(reg), offset, R1_SP); + } else if (reg.type() == StorageType::FLOAT) { + __ stfd(as_FloatRegister(reg), offset, R1_SP); + } else { + // stack and BAD + } } void RegSpiller::pd_load_reg(MacroAssembler* masm, int offset, VMStorage reg) { - Unimplemented(); + if (reg.type() == StorageType::INTEGER) { + __ ld(as_Register(reg), offset, R1_SP); + } else if (reg.type() == StorageType::FLOAT) { + __ lfd(as_FloatRegister(reg), offset, R1_SP); + } else { + // stack and BAD + } +} + +static int reg2offset(VMStorage vms, int stk_bias) { + assert(!vms.is_reg(), "wrong usage"); + return vms.index_or_offset() + stk_bias; +} + +static void move_reg64(MacroAssembler* masm, int out_stk_bias, + VMStorage from_reg, VMStorage to_reg) { + int out_bias = 0; + switch (to_reg.type()) { + case StorageType::INTEGER: + if (to_reg.segment_mask() == REG64_MASK && from_reg.segment_mask() == REG32_MASK) { + // see CCallingConventionRequiresIntsAsLongs + __ extsw(as_Register(to_reg), as_Register(from_reg)); + } else { + __ mr_if_needed(as_Register(to_reg), as_Register(from_reg)); + } + break; + case StorageType::FLOAT: + // FP arguments can get passed in GP reg! (Only in Upcall with HFA usage.) + assert(from_reg.segment_mask() == to_reg.segment_mask(), "sanity"); + if (to_reg.segment_mask() == REG32_MASK) { + __ stw(as_Register(from_reg), -8, R1_SP); + __ lfs(as_FloatRegister(to_reg), -8, R1_SP); // convert to double precision format + } else { + if (VM_Version::has_mtfprd()) { + __ mtfprd(as_FloatRegister(to_reg), as_Register(from_reg)); + } else { + __ std(as_Register(from_reg), -8, R1_SP); + __ lfd(as_FloatRegister(to_reg), -8, R1_SP); + } + } + break; + case StorageType::STACK: + out_bias = out_stk_bias; // fallthrough + case StorageType::FRAME_DATA: { + // Integer types always get a 64 bit slot in C. + Register storeval = as_Register(from_reg); + if (from_reg.segment_mask() == REG32_MASK) { + // see CCallingConventionRequiresIntsAsLongs + __ extsw(R0, as_Register(from_reg)); + storeval = R0; + } + switch (to_reg.stack_size()) { + case 8: __ std(storeval, reg2offset(to_reg, out_bias), R1_SP); break; + case 4: __ stw(storeval, reg2offset(to_reg, out_bias), R1_SP); break; + default: ShouldNotReachHere(); + } + } break; + default: ShouldNotReachHere(); + } +} + +static void move_float(MacroAssembler* masm, int out_stk_bias, + VMStorage from_reg, VMStorage to_reg) { + switch (to_reg.type()) { + case StorageType::INTEGER: + // FP arguments can get passed in GP reg! (Only for VarArgs for which we don't use FP regs.) + assert(from_reg.segment_mask() == to_reg.segment_mask(), "sanity"); + if (from_reg.segment_mask() == REG32_MASK) { + __ stfs(as_FloatRegister(from_reg), -8, R1_SP); // convert to single precision format + __ lwa(as_Register(to_reg), -8, R1_SP); + } else { + if (VM_Version::has_mtfprd()) { + __ mffprd(as_Register(to_reg), as_FloatRegister(from_reg)); + } else { + __ stfd(as_FloatRegister(from_reg), -8, R1_SP); + __ ld(as_Register(to_reg), -8, R1_SP); + } + } + break; + case StorageType::FLOAT: + __ fmr_if_needed(as_FloatRegister(to_reg), as_FloatRegister(from_reg)); + break; + case StorageType::STACK: + if (from_reg.segment_mask() == REG32_MASK) { + assert(to_reg.stack_size() == 4, "size should match"); + // TODO: Check if AIX needs 4 Byte offset + __ stfs(as_FloatRegister(from_reg), reg2offset(to_reg, out_stk_bias), R1_SP); + } else { + assert(to_reg.stack_size() == 8, "size should match"); + __ stfd(as_FloatRegister(from_reg), reg2offset(to_reg, out_stk_bias), R1_SP); + } + break; + default: ShouldNotReachHere(); + } +} + +static void move_stack(MacroAssembler* masm, Register callerSP, int in_stk_bias, int out_stk_bias, + VMStorage from_reg, VMStorage to_reg) { + int out_bias = 0; + switch (to_reg.type()) { + case StorageType::INTEGER: + switch (from_reg.stack_size()) { + case 8: __ ld( as_Register(to_reg), reg2offset(from_reg, in_stk_bias), callerSP); break; + case 4: __ lwa(as_Register(to_reg), reg2offset(from_reg, in_stk_bias), callerSP); break; + default: ShouldNotReachHere(); + } + break; + case StorageType::FLOAT: + switch (from_reg.stack_size()) { + case 8: __ lfd(as_FloatRegister(to_reg), reg2offset(from_reg, in_stk_bias), callerSP); break; + case 4: __ lfs(as_FloatRegister(to_reg), reg2offset(from_reg, in_stk_bias), callerSP); break; + default: ShouldNotReachHere(); + } + break; + case StorageType::STACK: + out_bias = out_stk_bias; // fallthrough + case StorageType::FRAME_DATA: { + switch (from_reg.stack_size()) { + case 8: __ ld( R0, reg2offset(from_reg, in_stk_bias), callerSP); break; + case 4: __ lwa(R0, reg2offset(from_reg, in_stk_bias), callerSP); break; + default: ShouldNotReachHere(); + } + switch (to_reg.stack_size()) { + case 8: __ std(R0, reg2offset(to_reg, out_bias), R1_SP); break; + case 4: __ stw(R0, reg2offset(to_reg, out_bias), R1_SP); break; + default: ShouldNotReachHere(); + } + } break; + default: ShouldNotReachHere(); + } } void ArgumentShuffle::pd_generate(MacroAssembler* masm, VMStorage tmp, int in_stk_bias, int out_stk_bias, const StubLocations& locs) const { - Unimplemented(); + Register callerSP = as_Register(tmp); // preset + for (int i = 0; i < _moves.length(); i++) { + Move move = _moves.at(i); + VMStorage from_reg = move.from; + VMStorage to_reg = move.to; + + // replace any placeholders + if (from_reg.type() == StorageType::PLACEHOLDER) { + from_reg = locs.get(from_reg); + } + if (to_reg.type() == StorageType::PLACEHOLDER) { + to_reg = locs.get(to_reg); + } + + switch (from_reg.type()) { + case StorageType::INTEGER: + move_reg64(masm, out_stk_bias, from_reg, to_reg); + break; + case StorageType::FLOAT: + move_float(masm, out_stk_bias, from_reg, to_reg); + break; + case StorageType::STACK: + move_stack(masm, callerSP, in_stk_bias, out_stk_bias, from_reg, to_reg); + break; + default: ShouldNotReachHere(); + } + } } diff --git a/src/hotspot/cpu/ppc/foreignGlobals_ppc.hpp b/src/hotspot/cpu/ppc/foreignGlobals_ppc.hpp index 3b18d64ed2b..baccdf2c9bb 100644 --- a/src/hotspot/cpu/ppc/foreignGlobals_ppc.hpp +++ b/src/hotspot/cpu/ppc/foreignGlobals_ppc.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2020 SAP SE. All rights reserved. - * 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) 2023 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,26 @@ #ifndef CPU_PPC_VM_FOREIGN_GLOBALS_PPC_HPP #define CPU_PPC_VM_FOREIGN_GLOBALS_PPC_HPP -class ABIDescriptor {}; +#include "asm/macroAssembler.hpp" +#include "utilities/growableArray.hpp" + +struct ABIDescriptor { + GrowableArray _integer_argument_registers; + GrowableArray _integer_return_registers; + GrowableArray _float_argument_registers; + GrowableArray _float_return_registers; + + GrowableArray _integer_additional_volatile_registers; + GrowableArray _float_additional_volatile_registers; + + int32_t _stack_alignment_bytes; + int32_t _shadow_space_bytes; + + VMStorage _scratch1; + VMStorage _scratch2; + + bool is_volatile_reg(Register reg) const; + bool is_volatile_reg(FloatRegister reg) const; +}; #endif // CPU_PPC_VM_FOREIGN_GLOBALS_PPC_HPP diff --git a/src/hotspot/cpu/ppc/frame_ppc.cpp b/src/hotspot/cpu/ppc/frame_ppc.cpp index 90fcbad8f27..2dcf9975477 100644 --- a/src/hotspot/cpu/ppc/frame_ppc.cpp +++ b/src/hotspot/cpu/ppc/frame_ppc.cpp @@ -206,13 +206,32 @@ frame frame::sender_for_entry_frame(RegisterMap *map) const { } UpcallStub::FrameData* UpcallStub::frame_data_for_frame(const frame& frame) const { - ShouldNotCallThis(); - return nullptr; + assert(frame.is_upcall_stub_frame(), "wrong frame"); + // need unextended_sp here, since normal sp is wrong for interpreter callees + return reinterpret_cast( + reinterpret_cast
(frame.unextended_sp()) + in_bytes(_frame_data_offset)); } bool frame::upcall_stub_frame_is_first() const { - ShouldNotCallThis(); - return false; + assert(is_upcall_stub_frame(), "must be optimzed entry frame"); + UpcallStub* blob = _cb->as_upcall_stub(); + JavaFrameAnchor* jfa = blob->jfa_for_frame(*this); + return jfa->last_Java_sp() == nullptr; +} + +frame frame::sender_for_upcall_stub_frame(RegisterMap* map) const { + assert(map != nullptr, "map must be set"); + UpcallStub* blob = _cb->as_upcall_stub(); + // Java frame called from C; skip all C frames and return top C + // frame of that chunk as the sender + JavaFrameAnchor* jfa = blob->jfa_for_frame(*this); + assert(!upcall_stub_frame_is_first(), "must have a frame anchor to go back to"); + assert(jfa->last_Java_sp() > sp(), "must be above this frame on stack"); + map->clear(); + assert(map->include_argument_oops(), "should be set by clear"); + frame fr(jfa->last_Java_sp(), jfa->last_Java_pc()); + + return fr; } frame frame::sender_for_interpreter_frame(RegisterMap *map) const { diff --git a/src/hotspot/cpu/ppc/frame_ppc.inline.hpp b/src/hotspot/cpu/ppc/frame_ppc.inline.hpp index c36b6c6e4da..478c0a9081a 100644 --- a/src/hotspot/cpu/ppc/frame_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/frame_ppc.inline.hpp @@ -281,8 +281,9 @@ inline frame frame::sender_raw(RegisterMap* map) const { return map->stack_chunk()->sender(*this, map); } - if (is_entry_frame()) return sender_for_entry_frame(map); - if (is_interpreted_frame()) return sender_for_interpreter_frame(map); + if (is_entry_frame()) return sender_for_entry_frame(map); + if (is_upcall_stub_frame()) return sender_for_upcall_stub_frame(map); + if (is_interpreted_frame()) return sender_for_interpreter_frame(map); assert(_cb == CodeCache::find_blob(pc()), "Must be the same"); if (_cb != nullptr) return sender_for_compiled_frame(map); diff --git a/src/hotspot/cpu/ppc/methodHandles_ppc.cpp b/src/hotspot/cpu/ppc/methodHandles_ppc.cpp index b75f48b6957..1ac9a35b6ba 100644 --- a/src/hotspot/cpu/ppc/methodHandles_ppc.cpp +++ b/src/hotspot/cpu/ppc/methodHandles_ppc.cpp @@ -308,7 +308,14 @@ address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* void MethodHandles::jump_to_native_invoker(MacroAssembler* _masm, Register nep_reg, Register temp_target) { BLOCK_COMMENT("jump_to_native_invoker {"); - __ stop("Should not reach here"); + assert_different_registers(nep_reg, temp_target); + assert(nep_reg != noreg, "required register"); + + // Load the invoker, as NEP -> .invoker + __ verify_oop(nep_reg); + __ ld(temp_target, NONZERO(jdk_internal_foreign_abi_NativeEntryPoint::downcall_stub_address_offset_in_bytes()), nep_reg); + __ mtctr(temp_target); + __ bctr(); BLOCK_COMMENT("} jump_to_native_invoker"); } diff --git a/src/hotspot/cpu/ppc/upcallLinker_ppc.cpp b/src/hotspot/cpu/ppc/upcallLinker_ppc.cpp index 914ea56851e..4cb86ad573c 100644 --- a/src/hotspot/cpu/ppc/upcallLinker_ppc.cpp +++ b/src/hotspot/cpu/ppc/upcallLinker_ppc.cpp @@ -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. + * Copyright (c) 2023 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,8 +23,100 @@ */ #include "precompiled.hpp" +#include "asm/macroAssembler.inline.hpp" +#include "logging/logStream.hpp" +#include "memory/resourceArea.hpp" #include "prims/upcallLinker.hpp" -#include "utilities/debug.hpp" +#include "runtime/sharedRuntime.hpp" +#include "runtime/signature.hpp" +#include "runtime/stubRoutines.hpp" +#include "utilities/formatBuffer.hpp" +#include "utilities/globalDefinitions.hpp" + +#define __ _masm-> + +// for callee saved regs, according to the caller's ABI +static int compute_reg_save_area_size(const ABIDescriptor& abi) { + int size = 0; + for (int i = 0; i < Register::number_of_registers; i++) { + Register reg = as_Register(i); + // R1 saved/restored by prologue/epilogue, R13 (system thread) won't get modified! + if (reg == R1_SP || reg == R13) continue; + if (!abi.is_volatile_reg(reg)) { + size += 8; // bytes + } + } + + for (int i = 0; i < FloatRegister::number_of_registers; i++) { + FloatRegister reg = as_FloatRegister(i); + if (!abi.is_volatile_reg(reg)) { + size += 8; // bytes + } + } + + return size; +} + +static void preserve_callee_saved_registers(MacroAssembler* _masm, const ABIDescriptor& abi, int reg_save_area_offset) { + // 1. iterate all registers in the architecture + // - check if they are volatile or not for the given abi + // - if NOT, we need to save it here + + int offset = reg_save_area_offset; + + __ block_comment("{ preserve_callee_saved_regs "); + for (int i = 0; i < Register::number_of_registers; i++) { + Register reg = as_Register(i); + // R1 saved/restored by prologue/epilogue, R13 (system thread) won't get modified! + if (reg == R1_SP || reg == R13) continue; + if (!abi.is_volatile_reg(reg)) { + __ std(reg, offset, R1_SP); + offset += 8; + } + } + + for (int i = 0; i < FloatRegister::number_of_registers; i++) { + FloatRegister reg = as_FloatRegister(i); + if (!abi.is_volatile_reg(reg)) { + __ stfd(reg, offset, R1_SP); + offset += 8; + } + } + + __ block_comment("} preserve_callee_saved_regs "); +} + +static void restore_callee_saved_registers(MacroAssembler* _masm, const ABIDescriptor& abi, int reg_save_area_offset) { + // 1. iterate all registers in the architecture + // - check if they are volatile or not for the given abi + // - if NOT, we need to restore it here + + int offset = reg_save_area_offset; + + __ block_comment("{ restore_callee_saved_regs "); + for (int i = 0; i < Register::number_of_registers; i++) { + Register reg = as_Register(i); + // R1 saved/restored by prologue/epilogue, R13 (system thread) won't get modified! + if (reg == R1_SP || reg == R13) continue; + if (!abi.is_volatile_reg(reg)) { + __ ld(reg, offset, R1_SP); + offset += 8; + } + } + + for (int i = 0; i < FloatRegister::number_of_registers; i++) { + FloatRegister reg = as_FloatRegister(i); + if (!abi.is_volatile_reg(reg)) { + __ lfd(reg, offset, R1_SP); + offset += 8; + } + } + + __ block_comment("} restore_callee_saved_regs "); +} + +static const int upcall_stub_code_base_size = 1536; // depends on GC (resolve_jobject) +static const int upcall_stub_size_per_arg = 16; // arg save & restore + move address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, BasicType* in_sig_bt, int total_in_args, @@ -32,6 +124,241 @@ address UpcallLinker::make_upcall_stub(jobject receiver, Method* entry, BasicType ret_type, jobject jabi, jobject jconv, bool needs_return_buffer, int ret_buf_size) { - ShouldNotCallThis(); - return nullptr; + ResourceMark rm; + const ABIDescriptor abi = ForeignGlobals::parse_abi_descriptor(jabi); + const CallRegs call_regs = ForeignGlobals::parse_call_regs(jconv); + int code_size = upcall_stub_code_base_size + (total_in_args * upcall_stub_size_per_arg); + CodeBuffer buffer("upcall_stub", code_size, /* locs_size = */ 1); + + Register callerSP = R2, // C/C++ uses R2 as TOC, but we can reuse it here + tmp = R11_scratch1, // same as shuffle_reg + call_target_address = R12_scratch2; // same as _abi._scratch2 + VMStorage shuffle_reg = abi._scratch1; + JavaCallingConvention out_conv; + NativeCallingConvention in_conv(call_regs._arg_regs); + ArgumentShuffle arg_shuffle(in_sig_bt, total_in_args, out_sig_bt, total_out_args, &in_conv, &out_conv, shuffle_reg); + // The Java call uses the JIT ABI, but we also call C. + int out_arg_area = MAX2(frame::jit_out_preserve_size + arg_shuffle.out_arg_bytes(), (int)frame::native_abi_reg_args_size); + +#ifndef PRODUCT + LogTarget(Trace, foreign, upcall) lt; + if (lt.is_enabled()) { + ResourceMark rm; + LogStream ls(lt); + arg_shuffle.print_on(&ls); + } +#endif + + int reg_save_area_size = compute_reg_save_area_size(abi); + RegSpiller arg_spiller(call_regs._arg_regs); + RegSpiller result_spiller(call_regs._ret_regs); + + int res_save_area_offset = out_arg_area; + int arg_save_area_offset = res_save_area_offset + result_spiller.spill_size_bytes(); + int reg_save_area_offset = arg_save_area_offset + arg_spiller.spill_size_bytes(); + int frame_data_offset = reg_save_area_offset + reg_save_area_size; + int frame_bottom_offset = frame_data_offset + sizeof(UpcallStub::FrameData); + + StubLocations locs; + int ret_buf_offset = -1; + if (needs_return_buffer) { + ret_buf_offset = frame_bottom_offset; + frame_bottom_offset += ret_buf_size; + // use a free register for shuffling code to pick up return + // buffer address from + locs.set(StubLocations::RETURN_BUFFER, abi._scratch2); + } + + int frame_size = align_up(frame_bottom_offset, StackAlignmentInBytes); + + // The space we have allocated will look like: + // + // + // FP-> | | + // |---------------------| = frame_bottom_offset = frame_size + // | (optional) | + // | ret_buf | + // |---------------------| = ret_buf_offset + // | | + // | FrameData | + // |---------------------| = frame_data_offset + // | | + // | reg_save_area | + // |---------------------| = reg_save_are_offset + // | | + // | arg_save_area | + // |---------------------| = arg_save_are_offset + // | | + // | res_save_area | + // |---------------------| = res_save_are_offset + // | | + // SP-> | out_arg_area | needs to be at end for shadow space + // + // + + ////////////////////////////////////////////////////////////////////////////// + + MacroAssembler* _masm = new MacroAssembler(&buffer); + address start = __ function_entry(); // called by C + __ save_LR_CR(R0); + assert((abi._stack_alignment_bytes % 16) == 0, "must be 16 byte aligned"); + // allocate frame (frame_size is also aligned, so stack is still aligned) + __ push_frame(frame_size, tmp); + + // we have to always spill args since we need to do a call to get the thread + // (and maybe attach it). + arg_spiller.generate_spill(_masm, arg_save_area_offset); + // Java methods won't preserve them, so save them here: + preserve_callee_saved_registers(_masm, abi, reg_save_area_offset); + + // Java code uses TOC (pointer to code cache). + __ load_const_optimized(R29_TOC, MacroAssembler::global_toc(), R0); // reinit + + __ block_comment("{ on_entry"); + __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::on_entry), R0); + __ addi(R3_ARG1, R1_SP, frame_data_offset); + __ call_c(call_target_address); + __ mr(R16_thread, R3_RET); + __ block_comment("} on_entry"); + + __ block_comment("{ argument shuffle"); + arg_spiller.generate_fill(_masm, arg_save_area_offset); + if (needs_return_buffer) { + assert(ret_buf_offset != -1, "no return buffer allocated"); + __ addi(as_Register(locs.get(StubLocations::RETURN_BUFFER)), R1_SP, ret_buf_offset); + } + __ ld(callerSP, _abi0(callers_sp), R1_SP); // preset (used to access caller frame argument slots) + arg_shuffle.generate(_masm, as_VMStorage(callerSP), frame::native_abi_minframe_size, frame::jit_out_preserve_size, locs); + __ block_comment("} argument shuffle"); + + __ block_comment("{ receiver "); + __ load_const_optimized(R3_ARG1, (intptr_t)receiver, R0); + __ resolve_jobject(R3_ARG1, tmp, R31, MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS); // kills R31 + __ block_comment("} receiver "); + + __ load_const_optimized(R19_method, (intptr_t)entry); + __ std(R19_method, in_bytes(JavaThread::callee_target_offset()), R16_thread); + + __ ld(call_target_address, in_bytes(Method::from_compiled_offset()), R19_method); + __ mtctr(call_target_address); + __ bctrl(); + + // return value shuffle + if (!needs_return_buffer) { + // CallArranger can pick a return type that goes in the same reg for both CCs. + if (call_regs._ret_regs.length() > 0) { // 0 or 1 + VMStorage ret_reg = call_regs._ret_regs.at(0); + // Check if the return reg is as expected. + switch (ret_type) { + case T_BOOLEAN: + case T_BYTE: + case T_SHORT: + case T_CHAR: + case T_INT: + __ extsw(R3_RET, R3_RET); // Clear garbage in high half. + // fallthrough + case T_LONG: + assert(as_Register(ret_reg) == R3_RET, "unexpected result register"); + break; + case T_FLOAT: + case T_DOUBLE: + assert(as_FloatRegister(ret_reg) == F1_RET, "unexpected result register"); + break; + default: + fatal("unexpected return type: %s", type2name(ret_type)); + } + } + } else { + // Load return values as required by UnboxBindingCalculator. + assert(ret_buf_offset != -1, "no return buffer allocated"); + int offset = ret_buf_offset; + for (int i = 0; i < call_regs._ret_regs.length(); i++) { + VMStorage reg = call_regs._ret_regs.at(i); + if (reg.type() == StorageType::INTEGER) { + // Load in matching size (not relevant for little endian). + if (reg.segment_mask() == REG32_MASK) { + __ lwa(as_Register(reg), offset, R1_SP); + } else { + __ ld(as_Register(reg), offset, R1_SP); + } + offset += 8; + } else if (reg.type() == StorageType::FLOAT) { + // Java code doesn't perform float-double format conversions. Do it here. + if (reg.segment_mask() == REG32_MASK) { + __ lfs(as_FloatRegister(reg), offset, R1_SP); + } else { + __ lfd(as_FloatRegister(reg), offset, R1_SP); + } + offset += 8; + } else { + ShouldNotReachHere(); + } + } + } + + result_spiller.generate_spill(_masm, res_save_area_offset); + + __ block_comment("{ on_exit"); + __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::on_exit), R0); + __ addi(R3_ARG1, R1_SP, frame_data_offset); + __ call_c(call_target_address); + __ block_comment("} on_exit"); + + restore_callee_saved_registers(_masm, abi, reg_save_area_offset); + + result_spiller.generate_fill(_masm, res_save_area_offset); + + __ pop_frame(); + __ restore_LR_CR(R0); + __ blr(); + + ////////////////////////////////////////////////////////////////////////////// + + __ block_comment("{ exception handler"); + + intptr_t exception_handler_offset = __ pc() - start; + + // Native caller has no idea how to handle exceptions, + // so we just crash here. Up to callee to catch exceptions. + __ verify_oop(R3_ARG1); + __ load_const_optimized(call_target_address, CAST_FROM_FN_PTR(uint64_t, UpcallLinker::handle_uncaught_exception), R0); + __ call_c(call_target_address); + __ should_not_reach_here(); + + __ block_comment("} exception handler"); + + _masm->flush(); + +#ifndef PRODUCT + stringStream ss; + ss.print("upcall_stub_%s", entry->signature()->as_C_string()); + const char* name = _masm->code_string(ss.as_string()); +#else // PRODUCT + const char* name = "upcall_stub"; +#endif // PRODUCT + + buffer.log_section_sizes(name); + + UpcallStub* blob + = UpcallStub::create(name, + &buffer, + exception_handler_offset, + receiver, + in_ByteSize(frame_data_offset)); +#ifndef ABI_ELFv2 + // Need to patch the FunctionDescriptor after relocating. + address fd_addr = blob->code_begin(); + FunctionDescriptor* fd = (FunctionDescriptor*)fd_addr; + fd->set_entry(fd_addr + sizeof(FunctionDescriptor)); +#endif + +#ifndef PRODUCT + if (lt.is_enabled()) { + ResourceMark rm; + LogStream ls(lt); + blob->print_on(&ls); + } +#endif + + return blob->code_begin(); } diff --git a/src/hotspot/cpu/ppc/vmstorage_ppc.hpp b/src/hotspot/cpu/ppc/vmstorage_ppc.hpp index 46efafebe0c..b569e3deb15 100644 --- a/src/hotspot/cpu/ppc/vmstorage_ppc.hpp +++ b/src/hotspot/cpu/ppc/vmstorage_ppc.hpp @@ -1,5 +1,6 @@ /* - * 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) 2023 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,25 +29,81 @@ #include "asm/register.hpp" +// keep in sync with jdk/internal/foreign/abi/ppc64/PPC64Architecture enum class StorageType : int8_t { - STACK = 0, - PLACEHOLDER = 1, + INTEGER = 0, + FLOAT = 1, + STACK = 2, + PLACEHOLDER = 3, // special locations used only by native code - FRAME_DATA = PLACEHOLDER + 1, + FRAME_DATA = 4, INVALID = -1 }; // need to define this before constructing VMStorage (below) constexpr inline bool VMStorage::is_reg(StorageType type) { - return false; + return type == StorageType::INTEGER || type == StorageType::FLOAT; } constexpr inline StorageType VMStorage::stack_type() { return StorageType::STACK; } constexpr inline StorageType VMStorage::placeholder_type() { return StorageType::PLACEHOLDER; } constexpr inline StorageType VMStorage::frame_data_type() { return StorageType::FRAME_DATA; } -inline VMStorage as_VMStorage(VMReg reg) { +// Needs to be consistent with PPC64Architecture.java. +constexpr uint16_t REG32_MASK = 0b0000000000000001; +constexpr uint16_t REG64_MASK = 0b0000000000000011; + +inline Register as_Register(VMStorage vms) { + assert(vms.type() == StorageType::INTEGER, "not the right type"); + return ::as_Register(vms.index()); +} + +inline FloatRegister as_FloatRegister(VMStorage vms) { + assert(vms.type() == StorageType::FLOAT, "not the right type"); + return ::as_FloatRegister(vms.index()); +} + +constexpr inline VMStorage as_VMStorage(Register reg, uint16_t segment_mask = REG64_MASK) { + return VMStorage::reg_storage(StorageType::INTEGER, segment_mask, reg.encoding()); +} + +constexpr inline VMStorage as_VMStorage(FloatRegister reg, uint16_t segment_mask = REG64_MASK) { + return VMStorage::reg_storage(StorageType::FLOAT, segment_mask, reg.encoding()); +} + +inline VMStorage as_VMStorage(VMReg reg, BasicType bt) { + if (reg->is_Register()) { + uint16_t segment_mask = 0; + switch (bt) { + case T_BOOLEAN: + case T_CHAR : + case T_BYTE : + case T_SHORT : + case T_INT : segment_mask = REG32_MASK; break; + default : segment_mask = REG64_MASK; break; + } + return as_VMStorage(reg->as_Register(), segment_mask); + } else if (reg->is_FloatRegister()) { + // FP regs always use double format. However, we need the correct format for loads /stores. + return as_VMStorage(reg->as_FloatRegister(), (bt == T_FLOAT) ? REG32_MASK : REG64_MASK); + } else if (reg->is_stack()) { + uint16_t size = 0; + switch (bt) { + case T_BOOLEAN: + case T_CHAR : + case T_BYTE : + case T_SHORT : + case T_INT : + case T_FLOAT : size = 4; break; + default : size = 8; break; + } + return VMStorage(StorageType::STACK, size, + checked_cast(reg->reg2stack() * VMRegImpl::stack_slot_size)); + } else if (!reg->is_valid()) { + return VMStorage::invalid(); + } + ShouldNotReachHere(); return VMStorage::invalid(); } -#endif // CPU_PPC_VMSTORAGE_PPC_INLINE_HPP \ No newline at end of file +#endif // CPU_PPC_VMSTORAGE_PPC_INLINE_HPP diff --git a/src/hotspot/cpu/riscv/vmstorage_riscv.hpp b/src/hotspot/cpu/riscv/vmstorage_riscv.hpp index b10b635898c..1275af27ccc 100644 --- a/src/hotspot/cpu/riscv/vmstorage_riscv.hpp +++ b/src/hotspot/cpu/riscv/vmstorage_riscv.hpp @@ -68,7 +68,7 @@ constexpr inline VMStorage as_VMStorage(FloatRegister reg) { return VMStorage::reg_storage(StorageType::FLOAT, FP_MASK, reg->encoding()); } -inline VMStorage as_VMStorage(VMReg reg) { +inline VMStorage as_VMStorage(VMReg reg, BasicType bt) { if (reg->is_Register()) { return as_VMStorage(reg->as_Register()); } else if (reg->is_FloatRegister()) { diff --git a/src/hotspot/cpu/s390/vmstorage_s390.hpp b/src/hotspot/cpu/s390/vmstorage_s390.hpp index 702e57efb94..192159adc4c 100644 --- a/src/hotspot/cpu/s390/vmstorage_s390.hpp +++ b/src/hotspot/cpu/s390/vmstorage_s390.hpp @@ -44,7 +44,7 @@ constexpr inline StorageType VMStorage::stack_type() { return StorageType::STACK constexpr inline StorageType VMStorage::placeholder_type() { return StorageType::PLACEHOLDER; } constexpr inline StorageType VMStorage::frame_data_type() { return StorageType::FRAME_DATA; } -inline VMStorage as_VMStorage(VMReg reg) { +inline VMStorage as_VMStorage(VMReg reg, BasicType bt) { ShouldNotReachHere(); return VMStorage::invalid(); } diff --git a/src/hotspot/cpu/x86/vmstorage_x86.hpp b/src/hotspot/cpu/x86/vmstorage_x86.hpp index a6ad21b2179..af5b0e8a306 100644 --- a/src/hotspot/cpu/x86/vmstorage_x86.hpp +++ b/src/hotspot/cpu/x86/vmstorage_x86.hpp @@ -82,7 +82,7 @@ constexpr inline VMStorage as_VMStorage(XMMRegister reg) { return VMStorage::reg_storage(StorageType::VECTOR, XMM_MASK, reg->encoding()); } -inline VMStorage as_VMStorage(VMReg reg) { +inline VMStorage as_VMStorage(VMReg reg, BasicType bt) { if (reg->is_Register()) { return as_VMStorage(reg->as_Register()); } else if (reg->is_XMMRegister()) { diff --git a/src/hotspot/cpu/zero/vmstorage_zero.hpp b/src/hotspot/cpu/zero/vmstorage_zero.hpp index 7536a6227e9..6a931b198bb 100644 --- a/src/hotspot/cpu/zero/vmstorage_zero.hpp +++ b/src/hotspot/cpu/zero/vmstorage_zero.hpp @@ -44,7 +44,7 @@ constexpr inline StorageType VMStorage::stack_type() { return StorageType::STACK constexpr inline StorageType VMStorage::placeholder_type() { return StorageType::PLACEHOLDER; } constexpr inline StorageType VMStorage::frame_data_type() { return StorageType::FRAME_DATA; } -inline VMStorage as_VMStorage(VMReg reg) { +inline VMStorage as_VMStorage(VMReg reg, BasicType bt) { ShouldNotReachHere(); return VMStorage::invalid(); } diff --git a/src/hotspot/share/prims/foreignGlobals.cpp b/src/hotspot/share/prims/foreignGlobals.cpp index 266d3b9e017..261be4ead59 100644 --- a/src/hotspot/share/prims/foreignGlobals.cpp +++ b/src/hotspot/share/prims/foreignGlobals.cpp @@ -176,7 +176,7 @@ int JavaCallingConvention::calling_convention(const BasicType* sig_bt, VMStorage // note, we ignore second here. Signature should consist of register-size values. So there should be // no need for multi-register pairs. //assert(!pair.first()->is_valid() || pair.is_single_reg(), "must be: %s"); - regs[i] = as_VMStorage(pair.first()); + regs[i] = as_VMStorage(pair.first(), sig_bt[i]); } return slots << LogBytesPerInt; } diff --git a/src/java.base/share/classes/jdk/internal/foreign/CABI.java b/src/java.base/share/classes/jdk/internal/foreign/CABI.java index 52ef0f68216..eee4ae67457 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/CABI.java +++ b/src/java.base/share/classes/jdk/internal/foreign/CABI.java @@ -39,6 +39,7 @@ public enum CABI { LINUX_AARCH_64, MAC_OS_AARCH_64, WIN_AARCH_64, + LINUX_PPC_64_LE, LINUX_RISCV_64, FALLBACK, UNSUPPORTED; @@ -72,6 +73,10 @@ public enum CABI { // The Linux ABI follows the standard AAPCS ABI return LINUX_AARCH_64; } + } else if (arch.equals("ppc64le")) { + if (OperatingSystem.isLinux()) { + return LINUX_PPC_64_LE; + } } else if (arch.equals("riscv64")) { if (OperatingSystem.isLinux()) { return LINUX_RISCV_64; diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java index d386287fed8..268fb8def43 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java @@ -30,6 +30,7 @@ import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker; import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker; import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64Linker; import jdk.internal.foreign.abi.fallback.FallbackLinker; +import jdk.internal.foreign.abi.ppc64.linux.LinuxPPC64leLinker; import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64Linker; import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker; import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker; @@ -57,8 +58,8 @@ import java.util.Objects; public abstract sealed class AbstractLinker implements Linker permits LinuxAArch64Linker, MacOsAArch64Linker, SysVx64Linker, WindowsAArch64Linker, - Windowsx64Linker, LinuxRISCV64Linker, - FallbackLinker { + Windowsx64Linker, LinuxPPC64leLinker, + LinuxRISCV64Linker, FallbackLinker { public interface UpcallStubFactory { MemorySegment makeStub(MethodHandle target, Arena arena); diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java b/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java index 1de4572cc51..b9f1de7ed64 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java @@ -33,6 +33,7 @@ import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker; import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker; import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64Linker; import jdk.internal.foreign.abi.fallback.FallbackLinker; +import jdk.internal.foreign.abi.ppc64.linux.LinuxPPC64leLinker; import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64Linker; import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker; import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker; @@ -236,6 +237,7 @@ public final class SharedUtils { case LINUX_AARCH_64 -> LinuxAArch64Linker.getInstance(); case MAC_OS_AARCH_64 -> MacOsAArch64Linker.getInstance(); case WIN_AARCH_64 -> WindowsAArch64Linker.getInstance(); + case LINUX_PPC_64_LE -> LinuxPPC64leLinker.getInstance(); case LINUX_RISCV_64 -> LinuxRISCV64Linker.getInstance(); case FALLBACK -> FallbackLinker.getInstance(); case UNSUPPORTED -> throw new UnsupportedOperationException("Platform does not support native linker"); diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/ABIv2CallArranger.java b/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/ABIv2CallArranger.java new file mode 100644 index 00000000000..c31204e8671 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/ABIv2CallArranger.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.internal.foreign.abi.ppc64; + +import jdk.internal.foreign.abi.ppc64.CallArranger; + +/** + * PPC64 CallArranger specialized for ABI v2. + */ +public class ABIv2CallArranger extends CallArranger { + // Currently no specific content, but CallArranger detects usage of ABIv2 for this class. +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/CallArranger.java b/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/CallArranger.java new file mode 100644 index 00000000000..6c26d67ae99 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/CallArranger.java @@ -0,0 +1,464 @@ +/* + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.internal.foreign.abi.ppc64; + +import jdk.internal.foreign.Utils; +import jdk.internal.foreign.abi.ABIDescriptor; +import jdk.internal.foreign.abi.AbstractLinker.UpcallStubFactory; +import jdk.internal.foreign.abi.Binding; +import jdk.internal.foreign.abi.CallingSequence; +import jdk.internal.foreign.abi.CallingSequenceBuilder; +import jdk.internal.foreign.abi.DowncallLinker; +import jdk.internal.foreign.abi.LinkerOptions; +import jdk.internal.foreign.abi.SharedUtils; +import jdk.internal.foreign.abi.VMStorage; +import jdk.internal.foreign.abi.ppc64.ABIv2CallArranger; + +import java.lang.foreign.AddressLayout; +import java.lang.foreign.FunctionDescriptor; +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.abi.ppc64.PPC64Architecture.*; +import static jdk.internal.foreign.abi.ppc64.PPC64Architecture.Regs.*; + +/** + * For the PPC64 C ABI specifically, this class uses CallingSequenceBuilder + * to translate a C FunctionDescriptor into a CallingSequence, which can then be turned into a MethodHandle. + * + * This includes taking care of synthetic arguments like pointers to return buffers for 'in-memory' returns. + * + * There are minor differences between the ABIs implemented on Linux and AIX + * which are handled in sub-classes. Clients should access these through the provided + * public constants CallArranger.ABIv1/2. + */ +public abstract class CallArranger { + final boolean useABIv2 = (this instanceof ABIv2CallArranger); + + private static final int STACK_SLOT_SIZE = 8; + private static final int MAX_COPY_SIZE = 8; + public static final int MAX_REGISTER_ARGUMENTS = 8; + public static final int MAX_FLOAT_REGISTER_ARGUMENTS = 13; + + // This is derived from the 64-Bit ELF v2 ABI spec, restricted to what's + // possible when calling to/from C code. (v1 is compatible, but uses fewer output registers.) + private final ABIDescriptor C = abiFor( + new VMStorage[] { r3, r4, r5, r6, r7, r8, r9, r10 }, // GP input + new VMStorage[] { f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13 }, // FP intput + new VMStorage[] { r3, r4 }, // GP output + new VMStorage[] { f1, f2, f3, f4, f5, f6, f7, f8 }, // FP output + new VMStorage[] { r0, r2, r11, r12 }, // volatile GP (excluding argument registers) + new VMStorage[] { f0 }, // volatile FP (excluding argument registers) + 16, // Stack is always 16 byte aligned on PPC64 + useABIv2 ? 32 : 48, // ABI header (excluding argument register spill slots) + r11, // scratch reg + r12 // target addr reg, otherwise used as scratch reg + ); + + public record Bindings(CallingSequence callingSequence, boolean isInMemoryReturn) {} + + private record HfaRegs(VMStorage[] first, VMStorage[] second) {} + + protected CallArranger() {} + + public static final CallArranger ABIv2 = new ABIv2CallArranger(); + + public Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall) { + return getBindings(mt, cDesc, forUpcall, LinkerOptions.empty()); + } + + public Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall, LinkerOptions options) { + CallingSequenceBuilder csb = new CallingSequenceBuilder(C, forUpcall, options); + + BindingCalculator argCalc = forUpcall ? new BoxBindingCalculator(true) : new UnboxBindingCalculator(true); + BindingCalculator retCalc = forUpcall ? new UnboxBindingCalculator(false) : new BoxBindingCalculator(false); + + boolean returnInMemory = isInMemoryReturn(cDesc.returnLayout()); + if (returnInMemory) { + Class carrier = MemorySegment.class; + MemoryLayout layout = SharedUtils.C_POINTER; + csb.addArgumentBindings(carrier, layout, argCalc.getBindings(carrier, layout)); + } else if (cDesc.returnLayout().isPresent()) { + Class carrier = mt.returnType(); + MemoryLayout layout = cDesc.returnLayout().get(); + csb.setReturnBindings(carrier, layout, retCalc.getBindings(carrier, layout)); + } + + for (int i = 0; i < mt.parameterCount(); i++) { + Class carrier = mt.parameterType(i); + MemoryLayout layout = cDesc.argumentLayouts().get(i); + if (options.isVarargsIndex(i)) { + argCalc.storageCalculator.adjustForVarArgs(); + } + csb.addArgumentBindings(carrier, layout, argCalc.getBindings(carrier, layout)); + } + + return new Bindings(csb.build(), returnInMemory); + } + + public MethodHandle arrangeDowncall(MethodType mt, FunctionDescriptor cDesc, LinkerOptions options) { + Bindings bindings = getBindings(mt, cDesc, false, options); + + MethodHandle handle = new DowncallLinker(C, bindings.callingSequence).getBoundMethodHandle(); + + if (bindings.isInMemoryReturn) { + handle = SharedUtils.adaptDowncallForIMR(handle, cDesc, bindings.callingSequence); + } + + return handle; + } + + 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, C, + bindings.callingSequence); + } + + private boolean isInMemoryReturn(Optional returnLayout) { + return returnLayout + .filter(GroupLayout.class::isInstance) + .filter(layout -> !TypeClass.isStructHFAorReturnRegisterAggregate(layout, useABIv2)) + .isPresent(); + } + + class StorageCalculator { + private final boolean forArguments; + + private final int[] nRegs = new int[] { 0, 0 }; + private long stackOffset = 0; + + public StorageCalculator(boolean forArguments) { + this.forArguments = forArguments; + } + + VMStorage stackAlloc(long size, long alignment) { + long alignedStackOffset = Utils.alignUp(stackOffset, alignment); + + short encodedSize = (short) size; + assert (encodedSize & 0xFFFF) == size; + + VMStorage storage = PPC64Architecture.stackStorage(encodedSize, (int) alignedStackOffset); + stackOffset = alignedStackOffset + size; + return storage; + } + + VMStorage regAlloc(int type) { + // GP regs always need to get reserved even when float regs are used. + int gpRegCnt = 1; + int fpRegCnt = (type == StorageType.INTEGER) ? 0 : 1; + + // Use stack if not enough registers available. + if (type == StorageType.FLOAT && nRegs[StorageType.FLOAT] + fpRegCnt > MAX_FLOAT_REGISTER_ARGUMENTS) { + type = StorageType.INTEGER; // Try gp reg. + } + if (type == StorageType.INTEGER && nRegs[StorageType.INTEGER] + gpRegCnt > MAX_REGISTER_ARGUMENTS) return null; + + VMStorage[] source = (forArguments ? C.inputStorage : C.outputStorage)[type]; + VMStorage result = source[nRegs[type]]; + + nRegs[StorageType.INTEGER] += gpRegCnt; + nRegs[StorageType.FLOAT] += fpRegCnt; + return result; + } + + // Integers need size for int to long conversion (required by ABI). + // FP loads and stores must use the correct IEEE 754 precision format (32/64 bit). + // Note: Can return a GP reg for a float! + VMStorage nextStorage(int type, boolean is32Bit) { + VMStorage reg = regAlloc(type); + // Stack layout computation: We need to count all arguments in order to get the correct + // offset for the next argument which will really use the stack. + // The reserved space for the Parameter Save Area is determined by the DowncallStubGenerator. + VMStorage stack; + if (!useABIv2 && is32Bit) { + stackAlloc(4, STACK_SLOT_SIZE); // Skip first half of stack slot. + stack = stackAlloc(4, 4); + } else { + stack = stackAlloc(is32Bit ? 4 : 8, STACK_SLOT_SIZE); + } + if (reg == null) return stack; + if (is32Bit) { + reg = new VMStorage(reg.type(), PPC64Architecture.REG32_MASK, reg.indexOrOffset()); + } + return reg; + } + + // Regular struct, no HFA. + VMStorage[] structAlloc(MemoryLayout layout) { + // TODO: Big Endian can't pass partially used slots correctly in some cases with: + // !useABIv2 && layout.byteSize() > 8 && layout.byteSize() % 8 != 0 + + // Allocate enough gp slots (regs and stack) such that the struct fits in them. + int numChunks = (int) Utils.alignUp(layout.byteSize(), MAX_COPY_SIZE) / MAX_COPY_SIZE; + VMStorage[] result = new VMStorage[numChunks]; + for (int i = 0; i < numChunks; i++) { + result[i] = nextStorage(StorageType.INTEGER, false); + } + return result; + } + + HfaRegs hfaAlloc(List scalarLayouts) { + // Determine count and type. + int count = scalarLayouts.size(); + Class elementCarrier = ((ValueLayout) (scalarLayouts.get(0))).carrier(); + int elementSize = (elementCarrier == float.class) ? 4 : 8; + + // Allocate registers. + int fpRegCnt = count; + // Rest will get put into a struct. Compute number of 64 bit slots. + int structSlots = 0; + boolean needOverlapping = false; // See "no partial DW rule" below. + + int availableFpRegs = MAX_FLOAT_REGISTER_ARGUMENTS - nRegs[StorageType.FLOAT]; + if (count > availableFpRegs) { + fpRegCnt = availableFpRegs; + int remainingElements = count - availableFpRegs; + if (elementCarrier == float.class) { + if ((fpRegCnt & 1) != 0) { + needOverlapping = true; + remainingElements--; // After overlapped one. + } + structSlots = (remainingElements + 1) / 2; + } else { + structSlots = remainingElements; + } + } + + VMStorage[] source = (forArguments ? C.inputStorage : C.outputStorage)[StorageType.FLOAT]; + VMStorage[] result = new VMStorage[fpRegCnt + structSlots], + result2 = new VMStorage[fpRegCnt + structSlots]; // For overlapping. + if (elementCarrier == float.class) { + // Mark elements as single precision (32 bit). + for (int i = 0; i < fpRegCnt; i++) { + VMStorage sourceReg = source[nRegs[StorageType.FLOAT] + i]; + result[i] = new VMStorage(StorageType.FLOAT, PPC64Architecture.REG32_MASK, + sourceReg.indexOrOffset()); + } + } else { + for (int i = 0; i < fpRegCnt; i++) { + result[i] = source[nRegs[StorageType.FLOAT] + i]; + } + } + + nRegs[StorageType.FLOAT] += fpRegCnt; + // Reserve GP regs and stack slots for the packed HFA (when using single precision). + int gpRegCnt = (elementCarrier == float.class) ? ((fpRegCnt + 1) / 2) + : fpRegCnt; + nRegs[StorageType.INTEGER] += gpRegCnt; + stackAlloc(fpRegCnt * elementSize, STACK_SLOT_SIZE); + + if (needOverlapping) { + // "no partial DW rule": Put GP reg or stack slot into result2. + // Note: Can only happen with forArguments = true. + VMStorage overlappingReg; + if (nRegs[StorageType.INTEGER] <= MAX_REGISTER_ARGUMENTS) { + VMStorage allocatedGpReg = C.inputStorage[StorageType.INTEGER][nRegs[StorageType.INTEGER] - 1]; + overlappingReg = new VMStorage(StorageType.INTEGER, + PPC64Architecture.REG64_MASK, allocatedGpReg.indexOrOffset()); + } else { + overlappingReg = new VMStorage(StorageType.STACK, + (short) STACK_SLOT_SIZE, (int) stackOffset - 4); + stackOffset += 4; // We now have a 64 bit slot, but reserved only 32 bit before. + } + result2[fpRegCnt - 1] = overlappingReg; + } + + // Allocate rest as struct. + for (int i = 0; i < structSlots; i++) { + result[fpRegCnt + i] = nextStorage(StorageType.INTEGER, false); + } + + return new HfaRegs(result, result2); + } + + void adjustForVarArgs() { + // PPC64 can pass VarArgs in GP regs. But we're not using FP regs. + nRegs[StorageType.FLOAT] = MAX_FLOAT_REGISTER_ARGUMENTS; + } + } + + abstract class BindingCalculator { + protected final StorageCalculator storageCalculator; + + protected BindingCalculator(boolean forArguments) { + this.storageCalculator = new StorageCalculator(forArguments); + } + + abstract List getBindings(Class carrier, MemoryLayout layout); + } + + // Compute recipe for transfering arguments / return values to C from Java. + class UnboxBindingCalculator extends BindingCalculator { + UnboxBindingCalculator(boolean forArguments) { + super(forArguments); + } + + @Override + List getBindings(Class carrier, MemoryLayout layout) { + TypeClass argumentClass = TypeClass.classifyLayout(layout, useABIv2); + Binding.Builder bindings = Binding.builder(); + switch (argumentClass) { + case STRUCT_REGISTER -> { + assert carrier == MemorySegment.class; + VMStorage[] regs = storageCalculator.structAlloc(layout); + long offset = 0; + for (VMStorage storage : regs) { + // Last slot may be partly used. + final long size = Math.min(layout.byteSize() - offset, MAX_COPY_SIZE); + Class type = SharedUtils.primitiveCarrierForSize(size, false); + if (offset + size < layout.byteSize()) { + bindings.dup(); + } + bindings.bufferLoad(offset, type, (int) size) + .vmStore(storage, type); + offset += size; + } + } + case STRUCT_HFA -> { + assert carrier == MemorySegment.class; + List scalarLayouts = TypeClass.scalarLayouts((GroupLayout) layout); + HfaRegs regs = storageCalculator.hfaAlloc(scalarLayouts); + final long baseSize = scalarLayouts.get(0).byteSize(); + long offset = 0; + for (int index = 0; index < regs.first().length; index++) { + VMStorage storage = regs.first()[index]; + // Floats are 4 Bytes, Double, GP reg and stack slots 8 Bytes (except maybe last slot). + long size = (baseSize == 4 && + (storage.type() == StorageType.FLOAT || layout.byteSize() - offset < 8)) ? 4 : 8; + Class type = SharedUtils.primitiveCarrierForSize(size, storage.type() == StorageType.FLOAT); + if (offset + size < layout.byteSize()) { + bindings.dup(); + } + bindings.bufferLoad(offset, type) + .vmStore(storage, type); + VMStorage storage2 = regs.second()[index]; + if (storage2 != null) { + // We have a second slot to fill (always 64 bit GP reg or stack slot). + size = 8; + if (offset + size < layout.byteSize()) { + bindings.dup(); + } + bindings.bufferLoad(offset, long.class) + .vmStore(storage2, long.class); + } + offset += size; + } + } + case POINTER -> { + VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, false); + bindings.unboxAddress() + .vmStore(storage, long.class); + } + case INTEGER -> { + // ABI requires all int types to get extended to 64 bit. + VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, false); + bindings.vmStore(storage, carrier); + } + case FLOAT -> { + VMStorage storage = storageCalculator.nextStorage(StorageType.FLOAT, carrier == float.class); + bindings.vmStore(storage, carrier); + } + default -> throw new UnsupportedOperationException("Unhandled class " + argumentClass); + } + return bindings.build(); + } + } + + // Compute recipe for transfering arguments / return values from C to Java. + class BoxBindingCalculator extends BindingCalculator { + BoxBindingCalculator(boolean forArguments) { + super(forArguments); + } + + @Override + List getBindings(Class carrier, MemoryLayout layout) { + TypeClass argumentClass = TypeClass.classifyLayout(layout, useABIv2); + Binding.Builder bindings = Binding.builder(); + switch (argumentClass) { + case STRUCT_REGISTER -> { + assert carrier == MemorySegment.class; + bindings.allocate(layout); + VMStorage[] regs = storageCalculator.structAlloc(layout); + long offset = 0; + for (VMStorage storage : regs) { + // Last slot may be partly used. + final long size = Math.min(layout.byteSize() - offset, MAX_COPY_SIZE); + Class type = SharedUtils.primitiveCarrierForSize(size, false); + bindings.dup() + .vmLoad(storage, type) + .bufferStore(offset, type, (int) size); + offset += size; + } + } + case STRUCT_HFA -> { + assert carrier == MemorySegment.class; + bindings.allocate(layout); + List scalarLayouts = TypeClass.scalarLayouts((GroupLayout) layout); + HfaRegs regs = storageCalculator.hfaAlloc(scalarLayouts); + final long baseSize = scalarLayouts.get(0).byteSize(); + long offset = 0; + for (int index = 0; index < regs.first().length; index++) { + // Use second if available since first one only contains one 32 bit value. + VMStorage storage = regs.second()[index] == null ? regs.first()[index] : regs.second()[index]; + // Floats are 4 Bytes, Double, GP reg and stack slots 8 Bytes (except maybe last slot). + final long size = (baseSize == 4 && + (storage.type() == StorageType.FLOAT || layout.byteSize() - offset < 8)) ? 4 : 8; + Class type = SharedUtils.primitiveCarrierForSize(size, storage.type() == StorageType.FLOAT); + bindings.dup() + .vmLoad(storage, type) + .bufferStore(offset, type); + offset += size; + } + } + case POINTER -> { + AddressLayout addressLayout = (AddressLayout) layout; + VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, false); + bindings.vmLoad(storage, long.class) + .boxAddressRaw(Utils.pointeeByteSize(addressLayout), Utils.pointeeByteAlign(addressLayout)); + } + case INTEGER -> { + // We could use carrier != long.class for BoxBindingCalculator, but C always uses 64 bit slots. + VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER, false); + bindings.vmLoad(storage, carrier); + } + case FLOAT -> { + VMStorage storage = storageCalculator.nextStorage(StorageType.FLOAT, carrier == float.class); + bindings.vmLoad(storage, carrier); + } + default -> throw new UnsupportedOperationException("Unhandled class " + argumentClass); + } + return bindings.build(); + } + } +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/PPC64Architecture.java b/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/PPC64Architecture.java new file mode 100644 index 00000000000..3a6eb89c9ec --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/PPC64Architecture.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.internal.foreign.abi.ppc64; + +import jdk.internal.foreign.abi.ABIDescriptor; +import jdk.internal.foreign.abi.Architecture; +import jdk.internal.foreign.abi.StubLocations; +import jdk.internal.foreign.abi.VMStorage; + +public final class PPC64Architecture implements Architecture { + public static final Architecture INSTANCE = new PPC64Architecture(); + + // Needs to be consistent with vmstorage_ppc.hpp. + public static final short REG32_MASK = 0b0000_0000_0000_0001; + public static final short REG64_MASK = 0b0000_0000_0000_0011; + + private static final int INTEGER_REG_SIZE = 8; + private static final int FLOAT_REG_SIZE = 8; + private static final int STACK_SLOT_SIZE = 8; + + // Suppresses default constructor, ensuring non-instantiability. + private PPC64Architecture() { + } + + @Override + public boolean isStackType(int cls) { + return cls == StorageType.STACK; + } + + @Override + public int typeSize(int cls) { + switch (cls) { + case StorageType.INTEGER: return INTEGER_REG_SIZE; + case StorageType.FLOAT: return FLOAT_REG_SIZE; + // STACK is deliberately omitted + } + + throw new IllegalArgumentException("Invalid Storage Class: " + cls); + } + + public interface StorageType { + byte INTEGER = 0; + byte FLOAT = 1; + byte STACK = 2; + byte PLACEHOLDER = 3; + } + + public static class Regs { // break circular dependency + public static final VMStorage r0 = integerRegister(0); + public static final VMStorage r1 = integerRegister(1); + public static final VMStorage r2 = integerRegister(2); + public static final VMStorage r3 = integerRegister(3); + public static final VMStorage r4 = integerRegister(4); + public static final VMStorage r5 = integerRegister(5); + public static final VMStorage r6 = integerRegister(6); + public static final VMStorage r7 = integerRegister(7); + public static final VMStorage r8 = integerRegister(8); + public static final VMStorage r9 = integerRegister(9); + public static final VMStorage r10 = integerRegister(10); + public static final VMStorage r11 = integerRegister(11); + public static final VMStorage r12 = integerRegister(12); + public static final VMStorage r13 = integerRegister(13); + public static final VMStorage r14 = integerRegister(14); + public static final VMStorage r15 = integerRegister(15); + public static final VMStorage r16 = integerRegister(16); + public static final VMStorage r17 = integerRegister(17); + public static final VMStorage r18 = integerRegister(18); + public static final VMStorage r19 = integerRegister(19); + public static final VMStorage r20 = integerRegister(20); + public static final VMStorage r21 = integerRegister(21); + public static final VMStorage r22 = integerRegister(22); + public static final VMStorage r23 = integerRegister(23); + public static final VMStorage r24 = integerRegister(24); + public static final VMStorage r25 = integerRegister(25); + public static final VMStorage r26 = integerRegister(26); + public static final VMStorage r27 = integerRegister(27); + public static final VMStorage r28 = integerRegister(28); + public static final VMStorage r29 = integerRegister(29); + public static final VMStorage r30 = integerRegister(30); + public static final VMStorage r31 = integerRegister(31); + + public static final VMStorage f0 = floatRegister(0); + public static final VMStorage f1 = floatRegister(1); + public static final VMStorage f2 = floatRegister(2); + public static final VMStorage f3 = floatRegister(3); + public static final VMStorage f4 = floatRegister(4); + public static final VMStorage f5 = floatRegister(5); + public static final VMStorage f6 = floatRegister(6); + public static final VMStorage f7 = floatRegister(7); + public static final VMStorage f8 = floatRegister(8); + public static final VMStorage f9 = floatRegister(9); + public static final VMStorage f10 = floatRegister(10); + public static final VMStorage f11 = floatRegister(11); + public static final VMStorage f12 = floatRegister(12); + public static final VMStorage f13 = floatRegister(13); + public static final VMStorage f14 = floatRegister(14); + public static final VMStorage f15 = floatRegister(15); + public static final VMStorage f16 = floatRegister(16); + public static final VMStorage f17 = floatRegister(17); + public static final VMStorage f18 = floatRegister(18); + public static final VMStorage f19 = floatRegister(19); + public static final VMStorage f20 = floatRegister(20); + public static final VMStorage f21 = floatRegister(21); + public static final VMStorage f22 = floatRegister(22); + public static final VMStorage f23 = floatRegister(23); + public static final VMStorage f24 = floatRegister(24); + public static final VMStorage f25 = floatRegister(25); + public static final VMStorage f26 = floatRegister(26); + public static final VMStorage f27 = floatRegister(27); + public static final VMStorage f28 = floatRegister(28); + public static final VMStorage f29 = floatRegister(29); + public static final VMStorage f30 = floatRegister(30); + public static final VMStorage f31 = floatRegister(31); + } + + private static VMStorage integerRegister(int index) { + return new VMStorage(StorageType.INTEGER, REG64_MASK, index, "r" + index); + } + + private static VMStorage floatRegister(int index) { + return new VMStorage(StorageType.FLOAT, REG64_MASK, index, "v" + index); + } + + public static VMStorage stackStorage(short size, int byteOffset) { + return new VMStorage(StorageType.STACK, size, byteOffset); + } + + public static ABIDescriptor abiFor(VMStorage[] inputIntRegs, + VMStorage[] inputFloatRegs, + VMStorage[] outputIntRegs, + VMStorage[] outputFloatRegs, + VMStorage[] volatileIntRegs, + VMStorage[] volatileFloatRegs, + int stackAlignment, + int shadowSpace, + VMStorage scratch1, VMStorage scratch2) { + return new ABIDescriptor( + INSTANCE, + new VMStorage[][] { + inputIntRegs, + inputFloatRegs, + }, + new VMStorage[][] { + outputIntRegs, + outputFloatRegs, + }, + new VMStorage[][] { + volatileIntRegs, + volatileFloatRegs, + }, + stackAlignment, + shadowSpace, + scratch1, scratch2, + StubLocations.TARGET_ADDRESS.storage(StorageType.PLACEHOLDER), + StubLocations.RETURN_BUFFER.storage(StorageType.PLACEHOLDER), + StubLocations.CAPTURED_STATE_BUFFER.storage(StorageType.PLACEHOLDER)); + } +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/TypeClass.java b/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/TypeClass.java new file mode 100644 index 00000000000..86dcf54cf41 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/TypeClass.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.internal.foreign.abi.ppc64; + +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SequenceLayout; +import java.lang.foreign.ValueLayout; +import java.util.List; +import java.util.ArrayList; + +public enum TypeClass { + STRUCT_REGISTER, + STRUCT_HFA, // Homogeneous Float Aggregate + POINTER, + INTEGER, + FLOAT; + + private static final int MAX_RETURN_AGGREGATE_REGS_SIZE = 2; + + private static TypeClass classifyValueType(ValueLayout type) { + Class carrier = type.carrier(); + if (carrier == boolean.class || carrier == byte.class || carrier == char.class || + carrier == short.class || carrier == int.class || carrier == long.class) { + return INTEGER; + } else if (carrier == float.class || carrier == double.class) { + return FLOAT; + } else if (carrier == MemorySegment.class) { + return POINTER; + } else { + throw new IllegalStateException("Cannot get here: " + carrier.getName()); + } + } + + static boolean isReturnRegisterAggregate(MemoryLayout type) { + return type.byteSize() <= MAX_RETURN_AGGREGATE_REGS_SIZE * 8; + } + + static List scalarLayouts(GroupLayout gl) { + List out = new ArrayList<>(); + scalarLayoutsInternal(out, gl); + return out; + } + + private static void scalarLayoutsInternal(List out, GroupLayout gl) { + for (MemoryLayout member : gl.memberLayouts()) { + if (member instanceof GroupLayout memberGl) { + scalarLayoutsInternal(out, memberGl); + } else if (member instanceof SequenceLayout memberSl) { + for (long i = 0; i < memberSl.elementCount(); i++) { + out.add(memberSl.elementLayout()); + } + } else { + // padding or value layouts + out.add(member); + } + } + } + + static boolean isHomogeneousFloatAggregate(MemoryLayout type, boolean useABIv2) { + List scalarLayouts = scalarLayouts((GroupLayout) type); + + final int numElements = scalarLayouts.size(); + if (numElements > (useABIv2 ? 8 : 1) || numElements == 0) + return false; + + MemoryLayout baseType = scalarLayouts.get(0); + + if (!(baseType instanceof ValueLayout)) + return false; + + TypeClass baseArgClass = classifyValueType((ValueLayout) baseType); + if (baseArgClass != FLOAT) + return false; + + for (MemoryLayout elem : scalarLayouts) { + if (!(elem instanceof ValueLayout)) + return false; + + TypeClass argClass = classifyValueType((ValueLayout) elem); + if (elem.byteSize() != baseType.byteSize() || + elem.byteAlignment() != baseType.byteAlignment() || + baseArgClass != argClass) { + return false; + } + } + + return true; + } + + private static TypeClass classifyStructType(MemoryLayout layout, boolean useABIv2) { + if (isHomogeneousFloatAggregate(layout, useABIv2)) { + return TypeClass.STRUCT_HFA; + } + return TypeClass.STRUCT_REGISTER; + } + + static boolean isStructHFAorReturnRegisterAggregate(MemoryLayout layout, boolean useABIv2) { + if (!(layout instanceof GroupLayout) || !useABIv2) return false; + return isHomogeneousFloatAggregate(layout, true) || isReturnRegisterAggregate(layout); + } + + public static TypeClass classifyLayout(MemoryLayout type, boolean useABIv2) { + if (type instanceof ValueLayout) { + return classifyValueType((ValueLayout) type); + } else if (type instanceof GroupLayout) { + return classifyStructType(type, useABIv2); + } else { + throw new IllegalArgumentException("Unhandled type " + type); + } + } +} diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/linux/LinuxPPC64leLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/linux/LinuxPPC64leLinker.java new file mode 100644 index 00000000000..e92b5abe26a --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/linux/LinuxPPC64leLinker.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.internal.foreign.abi.ppc64.linux; + +import jdk.internal.foreign.abi.AbstractLinker; +import jdk.internal.foreign.abi.LinkerOptions; +import jdk.internal.foreign.abi.ppc64.CallArranger; + +import java.lang.foreign.FunctionDescriptor; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; +import java.nio.ByteOrder; + +public final class LinuxPPC64leLinker extends AbstractLinker { + + public static LinuxPPC64leLinker getInstance() { + final class Holder { + private static final LinuxPPC64leLinker INSTANCE = new LinuxPPC64leLinker(); + } + + return Holder.INSTANCE; + } + + private LinuxPPC64leLinker() { + // Ensure there is only one instance + } + + @Override + protected MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function, LinkerOptions options) { + return CallArranger.ABIv2.arrangeDowncall(inferredMethodType, function, options); + } + + @Override + protected UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) { + return CallArranger.ABIv2.arrangeUpcall(targetType, function, options); + } + + @Override + protected ByteOrder linkerByteOrder() { + return ByteOrder.LITTLE_ENDIAN; + } +} diff --git a/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java b/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java index 25f18134127..a0dff95a23f 100644 --- a/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java +++ b/test/hotspot/jtreg/gc/shenandoah/compiler/TestLinkToNativeRBP.java @@ -28,7 +28,7 @@ * @summary guarantee(loc != NULL) failed: missing saved register with native invoke * * @requires vm.flavor == "server" - * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" + * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "ppc64le" * @requires vm.gc.Shenandoah * * @run main/othervm --enable-native-access=ALL-UNNAMED -XX:+UnlockDiagnosticVMOptions diff --git a/test/jdk/java/foreign/TestHFA.java b/test/jdk/java/foreign/TestHFA.java new file mode 100644 index 00000000000..c35a9391c23 --- /dev/null +++ b/test/jdk/java/foreign/TestHFA.java @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Test passing of Homogeneous Float Aggregates. + * @enablePreview + * @requires jdk.foreign.linker != "UNSUPPORTED" + * + * @run testng/othervm --enable-native-access=ALL-UNNAMED TestHFA + */ + +import java.lang.foreign.*; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import org.testng.annotations.Test; + +import static java.lang.foreign.ValueLayout.*; + +public class TestHFA { + + static { + System.loadLibrary("TestHFA"); + } + + final static Linker abi = Linker.nativeLinker(); + final static SymbolLookup lookup = SymbolLookup.loaderLookup(); + + static final OfFloat FLOAT = JAVA_FLOAT.withBitAlignment(32); + + final static GroupLayout S_FFLayout = MemoryLayout.structLayout( + FLOAT.withName("p0"), + FLOAT.withName("p1") + ).withName("S_FF"); + + final static GroupLayout S_FFFFFFFLayout = MemoryLayout.structLayout( + FLOAT.withName("p0"), + FLOAT.withName("p1"), + FLOAT.withName("p2"), + FLOAT.withName("p3"), + FLOAT.withName("p4"), + FLOAT.withName("p5"), + FLOAT.withName("p6") + ).withName("S_FFFF"); + + static final FunctionDescriptor fdadd_float_structs = FunctionDescriptor.of(S_FFFFFFFLayout, S_FFFFFFFLayout, S_FFFFFFFLayout); + static final FunctionDescriptor fdadd_float_to_struct_after_floats = FunctionDescriptor.of(S_FFLayout, + JAVA_FLOAT, JAVA_FLOAT, JAVA_FLOAT, JAVA_FLOAT, JAVA_FLOAT, + JAVA_FLOAT, JAVA_FLOAT, JAVA_FLOAT, JAVA_FLOAT, JAVA_FLOAT, + JAVA_FLOAT, JAVA_FLOAT, S_FFLayout, JAVA_FLOAT); + static final FunctionDescriptor fdadd_float_to_struct_after_structs = FunctionDescriptor.of(S_FFLayout, + S_FFLayout, S_FFLayout, S_FFLayout, S_FFLayout, S_FFLayout, S_FFLayout, + S_FFLayout, JAVA_FLOAT); + static final FunctionDescriptor fdadd_double_to_struct_after_structs = FunctionDescriptor.of(S_FFLayout, + S_FFLayout, S_FFLayout, S_FFLayout, S_FFLayout, S_FFLayout, S_FFLayout, + S_FFLayout, JAVA_DOUBLE); + static final FunctionDescriptor fdadd_float_to_large_struct_after_structs = FunctionDescriptor.of(S_FFFFFFFLayout, + S_FFLayout, S_FFLayout, S_FFLayout, S_FFLayout, S_FFLayout, S_FFLayout, + S_FFFFFFFLayout, JAVA_FLOAT); + + static final FunctionDescriptor fdpass_two_large_structs = FunctionDescriptor.of(S_FFFFFFFLayout, ADDRESS, S_FFFFFFFLayout, S_FFFFFFFLayout); + static final FunctionDescriptor fdpass_struct_after_floats = FunctionDescriptor.of(S_FFLayout, ADDRESS, S_FFLayout, JAVA_FLOAT); + static final FunctionDescriptor fdpass_struct_after_structs = FunctionDescriptor.of(S_FFLayout, ADDRESS, S_FFLayout, JAVA_FLOAT); + static final FunctionDescriptor fdpass_struct_after_structs_plus_double = FunctionDescriptor.of(S_FFLayout, ADDRESS, S_FFLayout, JAVA_DOUBLE); + static final FunctionDescriptor fdpass_large_struct_after_structs = FunctionDescriptor.of(S_FFFFFFFLayout, ADDRESS, S_FFFFFFFLayout, JAVA_FLOAT); + + final static MethodHandle mhadd_float_structs = abi.downcallHandle(lookup.find("add_float_structs").orElseThrow(), + fdadd_float_structs); + final static MethodHandle mhadd_float_to_struct_after_floats = abi.downcallHandle(lookup.find("add_float_to_struct_after_floats").orElseThrow(), + fdadd_float_to_struct_after_floats); + final static MethodHandle mhadd_float_to_struct_after_structs = abi.downcallHandle(lookup.find("add_float_to_struct_after_structs").orElseThrow(), + fdadd_float_to_struct_after_structs); + final static MethodHandle mhadd_double_to_struct_after_structs = abi.downcallHandle(lookup.find("add_double_to_struct_after_structs").orElseThrow(), + fdadd_double_to_struct_after_structs); + final static MethodHandle mhadd_float_to_large_struct_after_structs = abi.downcallHandle(lookup.find("add_float_to_large_struct_after_structs").orElseThrow(), + fdadd_float_to_large_struct_after_structs); + + final static MethodHandle mhpass_two_large_structs = abi.downcallHandle(lookup.find("pass_two_large_structs").orElseThrow(), + fdpass_two_large_structs); + final static MethodHandle mhpass_struct_after_floats = abi.downcallHandle(lookup.find("pass_struct_after_floats").orElseThrow(), + fdpass_struct_after_floats); + final static MethodHandle mhpass_struct_after_structs = abi.downcallHandle(lookup.find("pass_struct_after_structs").orElseThrow(), + fdpass_struct_after_structs); + final static MethodHandle mhpass_struct_after_structs_plus_double = abi.downcallHandle(lookup.find("pass_struct_after_structs_plus_double").orElseThrow(), + fdpass_struct_after_structs_plus_double); + final static MethodHandle mhpass_large_struct_after_structs = abi.downcallHandle(lookup.find("pass_large_struct_after_structs").orElseThrow(), + fdpass_large_struct_after_structs); + + @Test + public static void testAddFloatStructs() { + float p0 = 0.0f, p1 = 0.0f, p2 = 0.0f, p3 = 0.0f, p4 = 0.0f, p5 = 0.0f, p6 = 0.0f; + try { + Arena arena = Arena.ofConfined(); + MemorySegment s = arena.allocate(S_FFFFFFFLayout); + s.set(FLOAT, 0, 1.0f); + s.set(FLOAT, 4, 2.0f); + s.set(FLOAT, 8, 3.0f); + s.set(FLOAT, 12, 4.0f); + s.set(FLOAT, 16, 5.0f); + s.set(FLOAT, 20, 6.0f); + s.set(FLOAT, 24, 7.0f); + s = (MemorySegment)mhadd_float_structs.invokeExact((SegmentAllocator)arena, s, s); + p0 = s.get(FLOAT, 0); + p1 = s.get(FLOAT, 4); + p2 = s.get(FLOAT, 8); + p3 = s.get(FLOAT, 12); + p4 = s.get(FLOAT, 16); + p5 = s.get(FLOAT, 20); + p6 = s.get(FLOAT, 24); + System.out.println("S_FFFFFFF(" + p0 + ";" + p1 + ";" + p2 + ";" + p3 + ";" + p4 + ";" + p5 + ";" + p6 + ")"); + } catch (Throwable t) { + t.printStackTrace(); + } + if (p0 != 2.0f || p1 != 4.0f || p2 != 6.0f || p3 != 8.0f || p4 != 10.0f || p5 != 12.0f || p6 != 14.0f) + throw new RuntimeException("add_float_structs error"); + } + + @Test + public static void testAddFloatToStructAfterFloats() { + float p0 = 0.0f, p1 = 0.0f; + try { + Arena arena = Arena.ofConfined(); + MemorySegment s = arena.allocate(S_FFLayout); + s.set(FLOAT, 0, 1.0f); + s.set(FLOAT, 4, 1.0f); + s = (MemorySegment)mhadd_float_to_struct_after_floats.invokeExact((SegmentAllocator)arena, + 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, + 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, + 11.0f, 12.0f, s, 1.0f); + p0 = s.get(FLOAT, 0); + p1 = s.get(FLOAT, 4); + System.out.println("S_FF(" + p0 + ";" + p1 + ")"); + } catch (Throwable t) { + t.printStackTrace(); + } + if (p0 != 2.0f || p1 != 1.0f) throw new RuntimeException("add_float_to_struct_after_floats error"); + } + + @Test + public static void testAddFloatToStructAfterStructs() { + float p0 = 0.0f, p1 = 0.0f; + try { + Arena arena = Arena.ofConfined(); + MemorySegment s = arena.allocate(S_FFLayout); + s.set(FLOAT, 0, 1.0f); + s.set(FLOAT, 4, 1.0f); + s = (MemorySegment)mhadd_float_to_struct_after_structs.invokeExact((SegmentAllocator)arena, + s, s, s, s, s, s, + s, 1.0f); + p0 = s.get(FLOAT, 0); + p1 = s.get(FLOAT, 4); + System.out.println("S_FF(" + p0 + ";" + p1 + ")"); + } catch (Throwable t) { + t.printStackTrace(); + } + if (p0 != 2.0f || p1 != 1.0f) throw new RuntimeException("add_float_to_struct_after_structs error"); + } + + @Test + public static void testAddDoubleToStructAfterStructs() { + float p0 = 0.0f, p1 = 0.0f; + try { + Arena arena = Arena.ofConfined(); + MemorySegment s = arena.allocate(S_FFLayout); + s.set(FLOAT, 0, 1.0f); + s.set(FLOAT, 4, 1.0f); + s = (MemorySegment)mhadd_double_to_struct_after_structs.invokeExact((SegmentAllocator)arena, + s, s, s, s, s, s, + s, 1.0d); + p0 = s.get(FLOAT, 0); + p1 = s.get(FLOAT, 4); + System.out.println("S_FF(" + p0 + ";" + p1 + ")"); + } catch (Throwable t) { + t.printStackTrace(); + } + if (p0 != 2.0f || p1 != 1.0f) throw new RuntimeException("add_double_to_struct_after_structs error"); + } + + @Test + public static void testAddFloatToLargeStructAfterStructs() { + float p0 = 0.0f, p1 = 0.0f, p2 = 0.0f, p3 = 0.0f, p4 = 0.0f, p5 = 0.0f, p6 = 0.0f; + try { + Arena arena = Arena.ofConfined(); + MemorySegment s = arena.allocate(S_FFFFFFFLayout); + s.set(FLOAT, 0, 1.0f); + s.set(FLOAT, 4, 2.0f); + s.set(FLOAT, 8, 3.0f); + s.set(FLOAT, 12, 4.0f); + s.set(FLOAT, 16, 5.0f); + s.set(FLOAT, 20, 6.0f); + s.set(FLOAT, 24, 7.0f); + s = (MemorySegment)mhadd_float_to_large_struct_after_structs.invokeExact((SegmentAllocator)arena, + s, s, s, s, s, s, + s, 1.0f); + p0 = s.get(FLOAT, 0); + p1 = s.get(FLOAT, 4); + p2 = s.get(FLOAT, 8); + p3 = s.get(FLOAT, 12); + p4 = s.get(FLOAT, 16); + p5 = s.get(FLOAT, 20); + p6 = s.get(FLOAT, 24); + System.out.println("S_FFFFFFF(" + p0 + ";" + p1 + ";" + p2 + ";" + p3 + ";" + p4 + ";" + p5 + ";" + p6 + ")"); + } catch (Throwable t) { + t.printStackTrace(); + } + if (p0 != 2.0f || p1 != 2.0f || p2 != 3.0f || p3 != 4.0f || p4 != 5.0f || p5 != 6.0f || p6 != 7.0f) + throw new RuntimeException("add_float_to_large_struct_after_structs error"); + } + + // Java versions for Upcall tests. + public static MemorySegment addFloatStructs(MemorySegment p0, MemorySegment p1) { + float val0 = p0.get(FLOAT, 0) + p1.get(FLOAT, 0); + float val1 = p0.get(FLOAT, 4) + p1.get(FLOAT, 4); + float val2 = p0.get(FLOAT, 8) + p1.get(FLOAT, 8); + float val3 = p0.get(FLOAT, 12) + p1.get(FLOAT, 12); + float val4 = p0.get(FLOAT, 16) + p1.get(FLOAT, 16); + float val5 = p0.get(FLOAT, 20) + p1.get(FLOAT, 20); + float val6 = p0.get(FLOAT, 24) + p1.get(FLOAT, 24); + p0.set(FLOAT, 0, val0); + p0.set(FLOAT, 4, val1); + p0.set(FLOAT, 8, val2); + p0.set(FLOAT, 12, val3); + p0.set(FLOAT, 16, val4); + p0.set(FLOAT, 20, val5); + p0.set(FLOAT, 24, val6); + return p0; + } + + public static MemorySegment addFloatToStructAfterFloats( + float f1, float f2, float f3, float f4, float f5, + float f6, float f7, float f8, float f9, float f10, + float f11, float f12, MemorySegment s, float f) { + float val = s.get(FLOAT, 0); + s.set(FLOAT, 0, val + f); + return s; + } + + public static MemorySegment addFloatToStructAfterStructs( + MemorySegment s1, MemorySegment s2, MemorySegment s3, + MemorySegment s4, MemorySegment s5, MemorySegment s6, + MemorySegment s, float f) { + float val = s.get(FLOAT, 0); + s.set(FLOAT, 0, val + f); + return s; + } + + public static MemorySegment addDoubleToStructAfterStructs( + MemorySegment s1, MemorySegment s2, MemorySegment s3, + MemorySegment s4, MemorySegment s5, MemorySegment s6, + MemorySegment s, double f) { + float val = s.get(FLOAT, 0); + s.set(FLOAT, 0, val + (float) f); + return s; + } + + @Test + public static void testAddFloatStructsUpcall() { + float p0 = 0.0f, p1 = 0.0f, p2 = 0.0f, p3 = 0.0f, p4 = 0.0f, p5 = 0.0f, p6 = 0.0f; + try { + Arena arena = Arena.ofConfined(); + MemorySegment s = arena.allocate(S_FFFFFFFLayout); + s.set(FLOAT, 0, 1.0f); + s.set(FLOAT, 4, 2.0f); + s.set(FLOAT, 8, 3.0f); + s.set(FLOAT, 12, 4.0f); + s.set(FLOAT, 16, 5.0f); + s.set(FLOAT, 20, 6.0f); + s.set(FLOAT, 24, 7.0f); + MethodType mt = MethodType.methodType(MemorySegment.class, + MemorySegment.class, MemorySegment.class); + MemorySegment stub = abi.upcallStub(MethodHandles.lookup().findStatic(TestHFA.class, "addFloatStructs", mt), + fdadd_float_structs, arena); + s = (MemorySegment)mhpass_two_large_structs.invokeExact((SegmentAllocator)arena, stub, s, s); + p0 = s.get(FLOAT, 0); + p1 = s.get(FLOAT, 4); + p2 = s.get(FLOAT, 8); + p3 = s.get(FLOAT, 12); + p4 = s.get(FLOAT, 16); + p5 = s.get(FLOAT, 20); + p6 = s.get(FLOAT, 24); + System.out.println("S_FFFFFFF(" + p0 + ";" + p1 + ";" + p2 + ";" + p3 + ";" + p4 + ";" + p5 + ";" + p6 + ")"); + } catch (Throwable t) { + t.printStackTrace(); + } + if (p0 != 2.0f || p1 != 4.0f || p2 != 6.0f || p3 != 8.0f || p4 != 10.0f || p5 != 12.0f || p6 != 14.0f) + throw new RuntimeException("add_float_structs (Upcall)"); + } + + @Test + public static void testAddFloatToStructAfterFloatsUpcall() { + float p0 = 0.0f, p1 = 0.0f; + try { + Arena arena = Arena.ofConfined(); + MemorySegment s = arena.allocate(S_FFLayout); + s.set(FLOAT, 0, 1.0f); + s.set(FLOAT, 4, 1.0f); + MethodType mt = MethodType.methodType(MemorySegment.class, + float.class, float.class, float.class, float.class, + float.class, float.class, float.class, float.class, + float.class, float.class, float.class, float.class, + MemorySegment.class, float.class); + MemorySegment stub = abi.upcallStub(MethodHandles.lookup().findStatic(TestHFA.class, "addFloatToStructAfterFloats", mt), + fdadd_float_to_struct_after_floats, arena); + s = (MemorySegment)mhpass_struct_after_floats.invokeExact((SegmentAllocator)arena, stub, s, 1.0f); + p0 = s.get(FLOAT, 0); + p1 = s.get(FLOAT, 4); + System.out.println("S_FF(" + p0 + ";" + p1 + ")"); + } catch (Throwable t) { + t.printStackTrace(); + } + if (p0 != 2.0f || p1 != 1.0f) throw new RuntimeException("add_float_to_struct_after_floats (Upcall)"); + } + + @Test + public static void testAddFloatToStructAfterStructsUpcall() { + float p0 = 0.0f, p1 = 0.0f; + try { + Arena arena = Arena.ofConfined(); + MemorySegment s = arena.allocate(S_FFLayout); + s.set(FLOAT, 0, 1.0f); + s.set(FLOAT, 4, 1.0f); + MethodType mt = MethodType.methodType(MemorySegment.class, + MemorySegment.class, MemorySegment.class, MemorySegment.class, + MemorySegment.class, MemorySegment.class, MemorySegment.class, + MemorySegment.class, float.class); + MemorySegment stub = abi.upcallStub(MethodHandles.lookup().findStatic(TestHFA.class, "addFloatToStructAfterStructs", mt), + fdadd_float_to_struct_after_structs, arena); + s = (MemorySegment)mhpass_struct_after_structs.invokeExact((SegmentAllocator)arena, stub, s, 1.0f); + p0 = s.get(FLOAT, 0); + p1 = s.get(FLOAT, 4); + System.out.println("S_FF(" + p0 + ";" + p1 + ")"); + } catch (Throwable t) { + t.printStackTrace(); + } + if (p0 != 2.0f || p1 != 1.0f) throw new RuntimeException("add_float_to_struct_after_structs (Upcall)"); + } + + @Test + public static void testAddDoubleToStructAfterStructsUpcall() { + float p0 = 0.0f, p1 = 0.0f; + try { + Arena arena = Arena.ofConfined(); + MemorySegment s = arena.allocate(S_FFLayout); + s.set(FLOAT, 0, 1.0f); + s.set(FLOAT, 4, 1.0f); + MethodType mt = MethodType.methodType(MemorySegment.class, + MemorySegment.class, MemorySegment.class, MemorySegment.class, + MemorySegment.class, MemorySegment.class, MemorySegment.class, + MemorySegment.class, double.class); + MemorySegment stub = abi.upcallStub(MethodHandles.lookup().findStatic(TestHFA.class, "addDoubleToStructAfterStructs", mt), + fdadd_double_to_struct_after_structs, arena); + s = (MemorySegment)mhpass_struct_after_structs_plus_double.invokeExact((SegmentAllocator)arena, stub, s, 1.0d); + p0 = s.get(FLOAT, 0); + p1 = s.get(FLOAT, 4); + System.out.println("S_FF(" + p0 + ";" + p1 + ")"); + } catch (Throwable t) { + t.printStackTrace(); + } + if (p0 != 2.0f || p1 != 1.0f) throw new RuntimeException("add_double_to_struct_after_structs (Upcall)"); + } + + @Test + public static void testAddFloatToLargeStructAfterStructsUpcall() { + float p0 = 0.0f, p1 = 0.0f, p2 = 0.0f, p3 = 0.0f, p4 = 0.0f, p5 = 0.0f, p6 = 0.0f; + try { + Arena arena = Arena.ofConfined(); + MemorySegment s = arena.allocate(S_FFFFFFFLayout); + s.set(FLOAT, 0, 1.0f); + s.set(FLOAT, 4, 2.0f); + s.set(FLOAT, 8, 3.0f); + s.set(FLOAT, 12, 4.0f); + s.set(FLOAT, 16, 5.0f); + s.set(FLOAT, 20, 6.0f); + s.set(FLOAT, 24, 7.0f); + MethodType mt = MethodType.methodType(MemorySegment.class, + MemorySegment.class, MemorySegment.class, MemorySegment.class, + MemorySegment.class, MemorySegment.class, MemorySegment.class, + MemorySegment.class, float.class); + MemorySegment stub = abi.upcallStub(MethodHandles.lookup().findStatic(TestHFA.class, "addFloatToStructAfterStructs", mt), + fdadd_float_to_large_struct_after_structs, arena); + s = (MemorySegment)mhpass_large_struct_after_structs.invokeExact((SegmentAllocator)arena, stub, s, 1.0f); + p0 = s.get(FLOAT, 0); + p1 = s.get(FLOAT, 4); + p2 = s.get(FLOAT, 8); + p3 = s.get(FLOAT, 12); + p4 = s.get(FLOAT, 16); + p5 = s.get(FLOAT, 20); + p6 = s.get(FLOAT, 24); + System.out.println("S_FFFFFFF(" + p0 + ";" + p1 + ";" + p2 + ";" + p3 + ";" + p4 + ";" + p5 + ";" + p6 + ")"); + } catch (Throwable t) { + t.printStackTrace(); + } + if (p0 != 2.0f || p1 != 2.0f || p2 != 3.0f || p3 != 4.0f || p4 != 5.0f || p5 != 6.0f || p6 != 7.0f) + throw new RuntimeException("add_float_to_large_struct_after_structs (Upcall)"); + } +} diff --git a/test/jdk/java/foreign/callarranger/platform/PlatformLayouts.java b/test/jdk/java/foreign/callarranger/platform/PlatformLayouts.java index 45f7218c0ad..1646063fb08 100644 --- a/test/jdk/java/foreign/callarranger/platform/PlatformLayouts.java +++ b/test/jdk/java/foreign/callarranger/platform/PlatformLayouts.java @@ -199,6 +199,61 @@ public final class PlatformLayouts { } + /** + * This class defines layout constants modelling standard primitive types supported by the PPC64 ABI. + */ + public static final class PPC64 { + + private PPC64() { + //just the one + } + + /** + * The {@code bool} native type. + */ + public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN; + + /** + * The {@code char} native type. + */ + public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE; + + /** + * The {@code short} native type. + */ + public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT; + + /** + * The {@code int} native type. + */ + public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT; + + /** + * The {@code long} native type. + */ + public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG; + + /** + * The {@code long long} native type. + */ + public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG; + + /** + * The {@code float} native type. + */ + public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT; + + /** + * The {@code double} native type. + */ + public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE; + + /** + * The {@code T*} native type. + */ + public static final AddressLayout C_POINTER = SharedUtils.C_POINTER; + } + public static final class RISCV64 { // Suppresses default constructor, ensuring non-instantiability. diff --git a/test/jdk/java/foreign/libTestHFA.c b/test/jdk/java/foreign/libTestHFA.c new file mode 100644 index 00000000000..9b0ee2bdab8 --- /dev/null +++ b/test/jdk/java/foreign/libTestHFA.c @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023 SAP SE. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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. + */ + +#include "shared.h" + +struct S_FFFFFFF { float p0, p1, p2, p3, p4, p5, p6; }; + +EXPORT struct S_FFFFFFF add_float_structs(struct S_FFFFFFF p0, + struct S_FFFFFFF p1){ + p0.p0 += p1.p0; + p0.p1 += p1.p1; + p0.p2 += p1.p2; + p0.p3 += p1.p3; + p0.p4 += p1.p4; + p0.p5 += p1.p5; + p0.p6 += p1.p6; + return p0; +} + +// Corner case on PPC64le: Pass struct S_FF partially in FP register and on stack. +// Pass additional float on stack. +EXPORT struct S_FF add_float_to_struct_after_floats( + float f1, float f2, float f3, float f4, float f5, + float f6, float f7, float f8, float f9, float f10, + float f11, float f12, struct S_FF s, float f) { + s.p0 += f; + return s; +} + +// Corner case on PPC64le: Pass struct S_FF partially in FP register and in GP register. +// Pass additional float in GP register. +EXPORT struct S_FF add_float_to_struct_after_structs( + struct S_FF s1, struct S_FF s2, struct S_FF s3, struct S_FF s4, struct S_FF s5, struct S_FF s6, + struct S_FF s, float f) { + s.p0 += f; + return s; +} + +// Corner case on PPC64le: Pass struct S_FF partially in FP register and in GP register. +// Pass additional double in GP register. +EXPORT struct S_FF add_double_to_struct_after_structs( + struct S_FF s1, struct S_FF s2, struct S_FF s3, struct S_FF s4, struct S_FF s5, struct S_FF s6, + struct S_FF s, double f) { + s.p0 += (float)f; + return s; +} + +// Corner case on PPC64le: Pass struct S_FFFFFFF partially in FP register and in GP register and on stack. +EXPORT struct S_FFFFFFF add_float_to_large_struct_after_structs( + struct S_FF s1, struct S_FF s2, struct S_FF s3, struct S_FF s4, struct S_FF s5, struct S_FF s6, + struct S_FFFFFFF s, float f) { + s.p0 += f; + return s; +} + +// Upcall versions. +EXPORT struct S_FFFFFFF pass_two_large_structs(struct S_FFFFFFF (*fun)(struct S_FFFFFFF, struct S_FFFFFFF), + struct S_FFFFFFF s1, struct S_FFFFFFF s2) { + return fun(s1, s2); +} + +EXPORT struct S_FF pass_struct_after_floats(struct S_FF (*fun)( + float, float, float, float, float, + float, float, float, float, float, + float, float, struct S_FF, float), + struct S_FF s1, float f) { + return fun(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, s1, f); +} + +EXPORT struct S_FF pass_struct_after_structs(struct S_FF (*fun)( + struct S_FF, struct S_FF, struct S_FF, + struct S_FF, struct S_FF, struct S_FF, + struct S_FF, float), + struct S_FF s1, float f) { + struct S_FF dummy; + dummy.p0 = 1; dummy.p1 = 2; + return fun(dummy, dummy, dummy, dummy, dummy, dummy, s1, f); +} + +EXPORT struct S_FF pass_struct_after_structs_plus_double(struct S_FF (*fun)( + struct S_FF, struct S_FF, struct S_FF, + struct S_FF, struct S_FF, struct S_FF, + struct S_FF, double), + struct S_FF s1, double f) { + struct S_FF dummy; + dummy.p0 = 1; dummy.p1 = 2; + return fun(dummy, dummy, dummy, dummy, dummy, dummy, s1, f); +} + +EXPORT struct S_FFFFFFF pass_large_struct_after_structs(struct S_FFFFFFF (*fun)( + struct S_FF, struct S_FF, struct S_FF, + struct S_FF, struct S_FF, struct S_FF, + struct S_FFFFFFF, float), + struct S_FFFFFFF s1, float f) { + struct S_FF dummy; + dummy.p0 = 1; dummy.p1 = 2; + return fun(dummy, dummy, dummy, dummy, dummy, dummy, s1, f); +}