8303040: linux PPC64le: Implementation of Foreign Function & Memory API (Preview)
Reviewed-by: jvernee, rrich
This commit is contained in:
parent
466ec300fc
commit
20f15352a3
src
hotspot
cpu
aarch64
arm
ppc
downcallLinker_ppc.cppforeignGlobals_ppc.cppforeignGlobals_ppc.hppframe_ppc.cppframe_ppc.inline.hppmethodHandles_ppc.cppupcallLinker_ppc.cppvmstorage_ppc.hpp
riscv
s390
x86
zero
share/prims
java.base/share/classes/jdk/internal/foreign
test
hotspot/jtreg/gc/shenandoah/compiler
jdk/java/foreign
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
65
src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/linux/LinuxPPC64leLinker.java
Normal file
65
src/java.base/share/classes/jdk/internal/foreign/abi/ppc64/linux/LinuxPPC64leLinker.java
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023 SAP SE. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.internal.foreign.abi.ppc64.linux;
|
||||
|
||||
import jdk.internal.foreign.abi.AbstractLinker;
|
||||
import jdk.internal.foreign.abi.LinkerOptions;
|
||||
import jdk.internal.foreign.abi.ppc64.CallArranger;
|
||||
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
public final class 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
|
||||
|
420
test/jdk/java/foreign/TestHFA.java
Normal file
420
test/jdk/java/foreign/TestHFA.java
Normal file
@ -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.
|
||||
|
119
test/jdk/java/foreign/libTestHFA.c
Normal file
119
test/jdk/java/foreign/libTestHFA.c
Normal file
@ -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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user