8303040: linux PPC64le: Implementation of Foreign Function & Memory API (Preview)

Reviewed-by: jvernee, rrich
This commit is contained in:
Martin Doerr 2023-05-24 08:38:34 +00:00
parent 466ec300fc
commit 20f15352a3
27 changed files with 2485 additions and 47 deletions

@ -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()) {

@ -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();
}

@ -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<VMStorage>& _input_registers;
const GrowableArray<VMStorage>& _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<VMStorage>& input_registers,
const GrowableArray<VMStorage>& 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();
}

@ -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();
}
}
}

@ -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<Register> _integer_argument_registers;
GrowableArray<Register> _integer_return_registers;
GrowableArray<FloatRegister> _float_argument_registers;
GrowableArray<FloatRegister> _float_return_registers;
GrowableArray<Register> _integer_additional_volatile_registers;
GrowableArray<FloatRegister> _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

@ -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<UpcallStub::FrameData*>(
reinterpret_cast<address>(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 {

@ -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);

@ -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");
}

@ -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();
}

@ -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<uint16_t>(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
#endif // CPU_PPC_VMSTORAGE_PPC_INLINE_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()) {

@ -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();
}

@ -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()) {

@ -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();
}

@ -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;
}

@ -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;

@ -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);

@ -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");

@ -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.
}

@ -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<MemoryLayout> 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<MemoryLayout> 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<Binding> 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<Binding> 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<MemoryLayout> 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<Binding> 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<MemoryLayout> 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();
}
}
}

@ -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));
}
}

@ -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<MemoryLayout> scalarLayouts(GroupLayout gl) {
List<MemoryLayout> out = new ArrayList<>();
scalarLayoutsInternal(out, gl);
return out;
}
private static void scalarLayoutsInternal(List<MemoryLayout> 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<MemoryLayout> 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);
}
}
}

@ -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;
}
}

@ -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

@ -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)");
}
}

@ -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.

@ -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);
}