diff --git a/runtime/codert_vm/armnathelp.m4 b/runtime/codert_vm/armnathelp.m4 index a9d033d6e21..61356015f7f 100644 --- a/runtime/codert_vm/armnathelp.m4 +++ b/runtime/codert_vm/armnathelp.m4 @@ -366,6 +366,8 @@ UNUSED(jitNewPackedArray) UNUSED(jitResolvePackedArrayFieldLength) UNUSED(jitResolveIsPackedFieldNested) UNUSED(jitNewObjectNoTenantInit) +UNUSED(jitPostJNICallOffloadCheck) +UNUSED(jitPreJNICallOffloadCheck) UNUSED(jitFindFieldSignatureClass) UNUSED(icallVMprJavaSendInvokeWithArgumentsHelperL) UNUSED(j2iInvokeWithArguments) diff --git a/runtime/codert_vm/cnathelp.cpp b/runtime/codert_vm/cnathelp.cpp index 3af27ce9c80..dd565ca9129 100644 --- a/runtime/codert_vm/cnathelp.cpp +++ b/runtime/codert_vm/cnathelp.cpp @@ -2184,7 +2184,7 @@ old_slow_jitCallCFunction(J9VMThread *currentThread) } void -fast_jitPreJNICallOffloadCheck(J9VMThread *currentThread, J9Method *method) +fast_jitPreJNICallOffloadCheck(J9VMThread *currentThread) { #if defined(J9VM_OPT_JAVA_OFFLOAD_SUPPORT) OLD_JIT_HELPER_PROLOGUE(0); @@ -2196,12 +2196,12 @@ fast_jitPreJNICallOffloadCheck(J9VMThread *currentThread, J9Method *method) els->calloutVMState = setVMState(currentThread, J9VMSTATE_JNI_FROM_JIT); } #endif /* J9VM_PORT_ZOS_CEEHDLRSUPPORT */ - VM_VMHelpers::beforeJNICall(currentThread, method); + VM_VMHelpers::beforeJNICall(currentThread); #endif /* J9VM_OPT_JAVA_OFFLOAD_SUPPORT */ } void -fast_jitPostJNICallOffloadCheck(J9VMThread *currentThread, J9Method* method) +fast_jitPostJNICallOffloadCheck(J9VMThread *currentThread) { #if defined(J9VM_OPT_JAVA_OFFLOAD_SUPPORT) OLD_JIT_HELPER_PROLOGUE(0); @@ -2216,7 +2216,7 @@ fast_jitPostJNICallOffloadCheck(J9VMThread *currentThread, J9Method* method) } } #endif /* J9VM_PORT_ZOS_CEEHDLRSUPPORT */ - VM_VMHelpers::afterJNICall(currentThread, method); + VM_VMHelpers::afterJNICall(currentThread); #endif /* J9VM_OPT_JAVA_OFFLOAD_SUPPORT */ } diff --git a/runtime/codert_vm/pnathelp.m4 b/runtime/codert_vm/pnathelp.m4 index 53f4181228d..068dda8114f 100644 --- a/runtime/codert_vm/pnathelp.m4 +++ b/runtime/codert_vm/pnathelp.m4 @@ -411,6 +411,8 @@ UNUSED(jitNewPackedArray) UNUSED(jitResolvePackedArrayFieldLength) UNUSED(jitResolveIsPackedFieldNested) UNUSED(jitNewObjectNoTenantInit) +UNUSED(jitPostJNICallOffloadCheck) +UNUSED(jitPreJNICallOffloadCheck) UNUSED(jitFindFieldSignatureClass) UNUSED(icallVMprJavaSendInvokeWithArgumentsHelperL) UNUSED(j2iInvokeWithArguments) diff --git a/runtime/codert_vm/xnathelp.m4 b/runtime/codert_vm/xnathelp.m4 index 2eed368a659..4f81382a5f1 100644 --- a/runtime/codert_vm/xnathelp.m4 +++ b/runtime/codert_vm/xnathelp.m4 @@ -465,6 +465,8 @@ UNUSED(jitNewPackedArray) UNUSED(jitResolvePackedArrayFieldLength) UNUSED(jitResolveIsPackedFieldNested) UNUSED(jitNewObjectNoTenantInit) +UNUSED(jitPostJNICallOffloadCheck) +UNUSED(jitPreJNICallOffloadCheck) UNUSED(jitFindFieldSignatureClass) UNUSED(icallVMprJavaSendInvokeWithArgumentsHelperL) UNUSED(j2iInvokeWithArguments) diff --git a/runtime/codert_vm/znathelp.m4 b/runtime/codert_vm/znathelp.m4 index 54913e3e1ed..2780a451714 100644 --- a/runtime/codert_vm/znathelp.m4 +++ b/runtime/codert_vm/znathelp.m4 @@ -422,6 +422,38 @@ BEGIN_FUNC(jitRunOnJavaStack) BRANCH_VIA_VMTHREAD(J9TR_VMThread_tempSlot) END_CURRENT +dnl When the offload helpers are called, +dnl the java SP is already stored in the J9VMThread. + +BEGIN_HELPER(jitPreJNICallOffloadCheck) +ifdef({ASM_J9VM_PORT_ZOS_CEEHDLRSUPPORT},{ + std fpr8,JIT_FPR_SAVE_OFFSET(8)(CSP) + std fpr9,JIT_FPR_SAVE_OFFSET(9)(CSP) + std fpr10,JIT_FPR_SAVE_OFFSET(10)(CSP) + std fpr11,JIT_FPR_SAVE_OFFSET(11)(CSP) + std fpr12,JIT_FPR_SAVE_OFFSET(12)(CSP) + std fpr13,JIT_FPR_SAVE_OFFSET(13)(CSP) + std fpr14,JIT_FPR_SAVE_OFFSET(14)(CSP) + std fpr15,JIT_FPR_SAVE_OFFSET(15)(CSP) + stfpc CEEHDLR_FPC_SAVE_OFFSET(CSP) +}) + SAVE_ALL_REGS(jitPreJNICallOffloadCheck) + LR_GPR CARG1,J9VMTHREAD + LOAD_LABEL_CONSTANT($1, fast_jitPreJNICallOffloadCheck, CARG2) + CALL_INDIRECT(CARG2) + RESTORE_ALL_REGS_AND_SWITCH_TO_JAVA_STACK(jitPreJNICallOffloadCheck) + br r14 +END_CURRENT + +BEGIN_HELPER(jitPostJNICallOffloadCheck) + SAVE_ALL_REGS(jitPostJNICallOffloadCheck) + LR_GPR CARG1,J9VMTHREAD + LOAD_LABEL_CONSTANT($1, fast_jitPostJNICallOffloadCheck, CARG2) + CALL_INDIRECT(CARG2) + RESTORE_ALL_REGS_AND_SWITCH_TO_JAVA_STACK(jitPostJNICallOffloadCheck) + br r14 +END_CURRENT + dnl When the VM access helpers are called, dnl the java SP is already stored in the J9VMThread. diff --git a/runtime/compiler/build/files/target/z.mk b/runtime/compiler/build/files/target/z.mk index b00ac6d6594..612f03ca059 100644 --- a/runtime/compiler/build/files/target/z.mk +++ b/runtime/compiler/build/files/target/z.mk @@ -65,7 +65,6 @@ JIT_PRODUCT_SOURCE_FILES+=\ compiler/z/codegen/J9MemoryReference.cpp \ compiler/z/codegen/J9S390CHelperLinkage.cpp \ compiler/z/codegen/J9S390PrivateLinkage.cpp \ - compiler/z/codegen/J9S390JNILinkage.cpp \ compiler/z/codegen/J9S390Snippet.cpp \ compiler/z/codegen/J9S390SystemLinkage.cpp \ compiler/z/codegen/J9TreeEvaluator.cpp \ diff --git a/runtime/compiler/runtime/Runtime.cpp b/runtime/compiler/runtime/Runtime.cpp index 0f4e16a216d..13f33d9280b 100644 --- a/runtime/compiler/runtime/Runtime.cpp +++ b/runtime/compiler/runtime/Runtime.cpp @@ -195,9 +195,6 @@ extern "C" void mcc_reservationAdjustment_unwrapper(void **argsPtr, void **resPt extern "C" void mcc_lookupHelperTrampoline_unwrapper(void **argsPtr, void **resPtr); #endif -extern "C" void fast_jitPreJNICallOffloadCheck(J9VMThread *currentThread, J9Method *method); -extern "C" void fast_jitPostJNICallOffloadCheck(J9VMThread *currentThread, J9Method *method); - JIT_HELPER(icallVMprJavaSendNativeStatic); JIT_HELPER(icallVMprJavaSendStatic0); JIT_HELPER(icallVMprJavaSendStatic1); @@ -626,6 +623,8 @@ JIT_HELPER(_nativeStaticHelper); JIT_HELPER(jitLookupInterfaceMethod); JIT_HELPER(jitMethodIsNative); JIT_HELPER(jitMethodIsSync); +JIT_HELPER(jitPreJNICallOffloadCheck); +JIT_HELPER(jitPostJNICallOffloadCheck); JIT_HELPER(jitResolveClass); JIT_HELPER(jitResolveClassFromStaticField); JIT_HELPER(jitResolveField); @@ -1593,8 +1592,8 @@ void initializeCodeRuntimeHelperTable(J9JITConfig *jitConfig, char isSMP) SET(TR_S390arrayORHelper, (void *) 0, TR_Helper); SET(TR_S390arrayANDHelper, (void *) 0, TR_Helper); SET(TR_S390collapseJNIReferenceFrame, (void *) jitCollapseJNIReferenceFrame, TR_Helper); - SET(TR_S390jitPreJNICallOffloadCheck, (void *) fast_jitPreJNICallOffloadCheck, TR_CHelper); - SET(TR_S390jitPostJNICallOffloadCheck, (void *) fast_jitPostJNICallOffloadCheck, TR_CHelper); + SET(TR_S390jitPreJNICallOffloadCheck, (void *) jitPreJNICallOffloadCheck, TR_Helper); + SET(TR_S390jitPostJNICallOffloadCheck, (void *) jitPostJNICallOffloadCheck, TR_Helper); SET(TR_S390jitCallCFunction, (void *) jitCallCFunction, TR_Helper); SET(TR_S390OutlinedNew, (void *) outlinedNewObject, TR_Helper); SET(TR_S390OutlinedNewArray, (void *) outlinedNewArray, TR_Helper); diff --git a/runtime/compiler/runtime/Runtime.hpp b/runtime/compiler/runtime/Runtime.hpp index eff6e6648f8..2133f3e8b01 100644 --- a/runtime/compiler/runtime/Runtime.hpp +++ b/runtime/compiler/runtime/Runtime.hpp @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2018 IBM Corp. and others + * Copyright (c) 2000, 2017 IBM Corp. and others * * This program and the accompanying materials are made available under * the terms of the Eclipse Public License 2.0 which accompanies this @@ -87,12 +87,6 @@ inline uint32_t getJitEntryOffset(TR_LinkageInfo *linkageInfo) #define OFFSET_COUNTING_BRANCH_FROM_JITENTRY 36 #endif -#ifdef J9ZOS390 -#define TRS390_TOC_UNWRAP_ENV(wrappedPointer) (((J9FunctionDescriptor_T *) (wrappedPointer))->ada) -#else -#define TRS390_TOC_UNWRAP_ENV(wrappedPointer) (void*)0xdeafbeef -#endif - /* Functions used by AOT runtime to fixup recompilation info for AOT */ #if defined(TR_HOST_X86) || defined(TR_HOST_POWER) || defined(TR_HOST_S390) || (defined(TR_HOST_ARM)) uint32_t *getLinkageInfo(void * startPC); diff --git a/runtime/compiler/runtime/asmprotos.h b/runtime/compiler/runtime/asmprotos.h index 0963908a541..88cc1864f8b 100644 --- a/runtime/compiler/runtime/asmprotos.h +++ b/runtime/compiler/runtime/asmprotos.h @@ -86,6 +86,8 @@ JIT_HELPER(jitNewArray); // asm calling-convention helper JIT_HELPER(jitNewInstanceImplAccessCheck); // asm calling-convention helper JIT_HELPER(jitNewObject); // asm calling-convention helper JIT_HELPER(jitObjectHashCode); // asm calling-convention helper +JIT_HELPER(jitPostJNICallOffloadCheck); // asm calling-convention helper +JIT_HELPER(jitPreJNICallOffloadCheck); // asm calling-convention helper JIT_HELPER(jitReleaseVMAccess); // asm calling-convention helper JIT_HELPER(jitReportMethodEnter); // asm calling-convention helper JIT_HELPER(jitReportMethodExit); // asm calling-convention helper diff --git a/runtime/compiler/z/codegen/J9CodeGenerator.cpp b/runtime/compiler/z/codegen/J9CodeGenerator.cpp index 6fa43a53736..114886c591a 100644 --- a/runtime/compiler/z/codegen/J9CodeGenerator.cpp +++ b/runtime/compiler/z/codegen/J9CodeGenerator.cpp @@ -39,13 +39,13 @@ #include "env/jittypes.h" #include "il/Node.hpp" #include "il/Node_inlines.hpp" -#include "z/codegen/J9S390JNILinkage.hpp" #include "z/codegen/J9S390PrivateLinkage.hpp" #include "z/codegen/J9S390SystemLinkage.hpp" #include "z/codegen/J9S390CHelperLinkage.hpp" #include "z/codegen/S390GenerateInstructions.hpp" #include "z/codegen/S390Recompilation.hpp" #include "z/codegen/S390Register.hpp" +#include "z/codegen/J9S390PrivateLinkage.hpp" #include "z/codegen/ReduceSynchronizedFieldLoad.hpp" #define OPT_DETAILS "O^O CODE GENERATION: " diff --git a/runtime/compiler/z/codegen/J9S390CHelperLinkage.cpp b/runtime/compiler/z/codegen/J9S390CHelperLinkage.cpp index 2811abb4bb3..4cc9abad9d1 100644 --- a/runtime/compiler/z/codegen/J9S390CHelperLinkage.cpp +++ b/runtime/compiler/z/codegen/J9S390CHelperLinkage.cpp @@ -247,17 +247,21 @@ class RealRegisterManager TR::CodeGenerator* _cg; }; -TR::Register * TR::S390CHelperLinkage::buildDirectDispatch(TR::Node * callNode, - TR::RegisterDependencyConditions **deps, - TR::Register *returnReg, - bool forceFastPath) +/** \brief Build a JIT helper call. + * \details + * It generates sequence that prepares parameters for the JIT helper function and generate a helper call. + * \param callNode The node for which you are generating a heleper call + * \param deps The pre register dependency conditions that will be filled by this function to attach within ICF + * \param returnReg TR::Register* allocated by consumer of this API to hold the result of the helper call, + * If passed, this function uses it to store return value from helper instead of allocating new register + * \return TR::Register *helperReturnResult, gets the return value of helper function and return to the evaluator. + */ +TR::Register * TR::S390CHelperLinkage::buildDirectDispatch(TR::Node * callNode, TR::RegisterDependencyConditions **deps, TR::Register *returnReg) { RealRegisterManager RealRegisters(cg()); bool isHelperCallWithinICF = deps != NULL; - // TODO: Currently only jitInstanceOf and IFA helper calls are fast path helpers. Need to modify following condition if we add support for other fast path only helpers - // Having a fast path helper call also implies that there is no environment pointer setup in GPR5 - bool isFastPathOnly = callNode->getOpCodeValue() == TR::instanceof || forceFastPath; - + // TODO: Currently only jitInstanceOf is fast path helper. Need to modify following condition if we add support for other fast path only helpers + bool isFastPathOnly = callNode->getOpCodeValue() == TR::instanceof; traceMsg(comp(),"%s: Internal Control Flow in OOL : %s\n",callNode->getOpCode().getName(),isHelperCallWithinICF ? "true" : "false" ); for (int i = TR::RealRegister::FirstGPR; i <= TR::RealRegister::LastHPR; i++) { @@ -328,7 +332,6 @@ TR::Register * TR::S390CHelperLinkage::buildDirectDispatch(TR::Node * callNode, // Storing Java Stack Pointer javaStackPointerRegister = cg()->getStackPointerRealRegister(); cursor = generateRXInstruction(cg(), TR::InstOpCode::getStoreOpCode(), callNode, javaStackPointerRegister, generateS390MemoryReference(vmThreadRegister, offsetJ9SP, cg())); - #if defined(J9ZOS390) padding += 2; // Loading DSAPointer Register @@ -349,21 +352,6 @@ TR::Register * TR::S390CHelperLinkage::buildDirectDispatch(TR::Node * callNode, } TR::SymbolReference * callSymRef = callNode->getSymbolReference(); void * destAddr = callNode->getSymbolReference()->getSymbol()->castToMethodSymbol()->getMethodAddress(); - -#if defined(J9ZOS390) - // Fast path helper calls are real C function calls, whereas non-fast-path calls jump to picBuilder glue code - // glue code does its own environment handling and does not need a environment loading here. - if(isFastPathOnly) - { - TR_RuntimeHelper helperIndex = static_cast(callNode->getSymbolReference()->getReferenceNumber()); - TR_ASSERT_FATAL(helperIndex < TR_S390numRuntimeHelpers, "Invalid helper index in c helper node\n"); - - // XPLINK GPR5 is an environment register. - intptrj_t environment = reinterpret_cast(TOC_UNWRAP_ENV(runtimeHelpers.getFunctionPointer(helperIndex))); - genLoadAddressConstant(cg(), callNode, environment, javaStackPointerRegister); - } -#endif - cursor = new (cg()->trHeapMemory()) TR::S390RILInstruction(TR::InstOpCode::BRASL, callNode, regRA, destAddr, callSymRef, cg()); cursor->setDependencyConditions(preDeps); if (isFastPathOnly) diff --git a/runtime/compiler/z/codegen/J9S390CHelperLinkage.hpp b/runtime/compiler/z/codegen/J9S390CHelperLinkage.hpp index 1f7565dc492..6f87c11393d 100644 --- a/runtime/compiler/z/codegen/J9S390CHelperLinkage.hpp +++ b/runtime/compiler/z/codegen/J9S390CHelperLinkage.hpp @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2018 IBM Corp. and others + * Copyright (c) 2000, 2017 IBM Corp. and others * * This program and the accompanying materials are made available under * the terms of the Eclipse Public License 2.0 which accompanies this @@ -122,21 +122,6 @@ class S390CHelperLinkage : public TR::Linkage return buildDirectDispatch(callNode, NULL, returnReg); } - /** \brief - * It generates a sequence that prepares parameters for the JIT helper function and generate a - * direct call to a C helper function. - * - * \param callNode The node for which you are generating a heleper call - * \param deps The pre register dependency conditions that will be filled by this function to attach within ICF - * \param returnReg TR::Register* allocated by consumer of this API to hold the result of the helper call, - * If passed, this function uses it to store return value from helper instead of allocating new register - * \param forceFastPath A flag to indicate that the given TR_RuntimeHelper should be built with fast path. Fast path - * helpers have no internal control flow. - * \return TR::Register *helperReturnResult, gets the return value of helper function and return to the evaluator. - */ - TR::Register *buildDirectDispatch(TR::Node *callNode, - TR::RegisterDependencyConditions** deps, - TR::Register *returnReg=NULL, - bool forceFastPath = false); + TR::Register *buildDirectDispatch(TR::Node *callNode, TR::RegisterDependencyConditions** deps, TR::Register *returnReg=NULL); }; } diff --git a/runtime/compiler/z/codegen/J9S390JNILinkage.cpp b/runtime/compiler/z/codegen/J9S390JNILinkage.cpp deleted file mode 100644 index f35d762a097..00000000000 --- a/runtime/compiler/z/codegen/J9S390JNILinkage.cpp +++ /dev/null @@ -1,1014 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2018, 2018 IBM Corp. and others - * - * This program and the accompanying materials are made available under - * the terms of the Eclipse Public License 2.0 which accompanies this - * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ - * or the Apache License, Version 2.0 which accompanies this distribution and - * is available at https://www.apache.org/licenses/LICENSE-2.0. - * - * This Source Code may also be made available under the following - * Secondary Licenses when the conditions for such availability set - * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU - * General Public License, version 2 with the GNU Classpath - * Exception [1] and GNU General Public License, version 2 with the - * OpenJDK Assembly Exception [2]. - * - * [1] https://www.gnu.org/software/classpath/license.html - * [2] http://openjdk.java.net/legal/assembly-exception.html - * - * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception - *******************************************************************************/ - -#include "z/codegen/J9S390JNILinkage.hpp" - -#include "codegen/CodeGenerator.hpp" -#include "compile/ResolvedMethod.hpp" -#include "env/CompilerEnv.hpp" -#include "env/VMJ9.h" -#include "env/jittypes.h" -#include "env/j9method.h" -#include "il/Node.hpp" -#include "il/Node_inlines.hpp" -#include "il/TreeTop.hpp" -#include "il/TreeTop_inlines.hpp" -#include "z/codegen/S390Evaluator.hpp" -#include "z/codegen/S390GenerateInstructions.hpp" -#include "z/codegen/S390HelperCallSnippet.hpp" -#include "z/codegen/S390J9CallSnippet.hpp" -#include "z/codegen/TRSystemLinkage.hpp" -#include "z/codegen/J9S390CHelperLinkage.hpp" - -#define JNI_CALLOUT_FRAME_SIZE 5*sizeof(intptrj_t) - -TR::J9S390JNILinkage::J9S390JNILinkage(TR::CodeGenerator * cg, TR_S390LinkageConventions elc, TR_LinkageConventions lc) - :TR::S390PrivateLinkage(cg, elc, lc) - { - } - -void TR::J9S390JNILinkage::checkException(TR::Node * callNode, - TR::Register* javaLitOffsetReg, - TR::Register* methodMetaDataVirtualRegister) - { - TR_J9VMBase *fej9 = static_cast(fe()); - - generateRXInstruction(cg(), TR::InstOpCode::getLoadOpCode(), callNode, - javaLitOffsetReg, - generateS390MemoryReference(methodMetaDataVirtualRegister, - fej9->thisThreadGetCurrentExceptionOffset(), - cg())); - - // check exception - TR::LabelSymbol * exceptionRestartLabel = generateLabelSymbol(self()->cg()); - TR::LabelSymbol * exceptionSnippetLabel = generateLabelSymbol(self()->cg()); - - TR::Instruction *gcPoint = generateS390CompareAndBranchInstruction(self()->cg(), - TR::InstOpCode::getCmpOpCode(), callNode, - javaLitOffsetReg, - (signed char)0, - TR::InstOpCode::COND_BNE, - exceptionSnippetLabel, false, true); - gcPoint->setNeedsGCMap(0); - - self()->cg()->addSnippet(new (self()->trHeapMemory()) TR::S390HelperCallSnippet(self()->cg(), callNode, exceptionSnippetLabel, - self()->comp()->getSymRefTab()->findOrCreateThrowCurrentExceptionSymbolRef(self()->comp()->getJittedMethodSymbol()), exceptionRestartLabel)); - generateS390LabelInstruction(self()->cg(), TR::InstOpCode::LABEL, callNode, exceptionRestartLabel); - } - -void -TR::J9S390JNILinkage::collapseJNIReferenceFrame(TR::Node * callNode, - TR::Register* javaLitPoolRealRegister, - TR::Register* methodAddressReg, - TR::Register* javaStackPointerRealRegister) - { - TR_J9VMBase *fej9 = static_cast(fe()); - - intptr_t flagValue = fej9->constJNIReferenceFrameAllocatedFlags(); - genLoadAddressConstant(cg(), callNode, flagValue, methodAddressReg, NULL, NULL, javaLitPoolRealRegister); - generateRXInstruction(cg(), TR::InstOpCode::getAndOpCode(), callNode, - methodAddressReg, - generateS390MemoryReference(javaStackPointerRealRegister, - fej9->constJNICallOutFrameFlagsOffset() - JNI_CALLOUT_FRAME_SIZE, - cg())); - - // must check to see if the ref pool was used and clean them up if so--or we - // leave a bunch of pinned garbage behind that screws up the gc quality forever - TR::LabelSymbol * refPoolRestartLabel = generateLabelSymbol(cg()); - TR::LabelSymbol * refPoolSnippetLabel = generateLabelSymbol(cg()); - - TR::Instruction *gcPoint = generateS390CompareAndBranchInstruction(self()->cg(), - TR::InstOpCode::getCmpOpCode(), - callNode, methodAddressReg, - (signed char)0, - TR::InstOpCode::COND_BNE, - refPoolSnippetLabel, false, true); - gcPoint->setNeedsGCMap(0); - - TR::SymbolReference * collapseSymRef = cg()->symRefTab()->findOrCreateRuntimeHelper(TR_S390collapseJNIReferenceFrame, - false, false, false); - - cg()->addSnippet(new (trHeapMemory()) TR::S390HelperCallSnippet(cg(), callNode, - refPoolSnippetLabel, collapseSymRef, refPoolRestartLabel)); - generateS390LabelInstruction(cg(), TR::InstOpCode::LABEL, callNode, refPoolRestartLabel); - } - -void -TR::J9S390JNILinkage::unwrapReturnValue(TR::Node* callNode, - TR::Register* javaReturnRegister, - TR::RegisterDependencyConditions* deps) - { - TR::LabelSymbol * tempLabel = generateLabelSymbol(cg()); - TR::Instruction* start = generateS390CompareAndBranchInstruction(cg(), TR::InstOpCode::getCmpOpCode(), - callNode, javaReturnRegister, - (signed char)0, TR::InstOpCode::COND_BE, tempLabel); - - start->setStartInternalControlFlow(); - generateRXInstruction(cg(), TR::InstOpCode::getLoadOpCode(), callNode, javaReturnRegister, - generateS390MemoryReference(javaReturnRegister, 0, cg())); - - int32_t regPos = deps->searchPostConditionRegisterPos(javaReturnRegister); - TR::RealRegister::RegNum realReg = deps->getPostConditions()->getRegisterDependency(regPos)->getRealRegister(); - TR::RegisterDependencyConditions * postDeps = new (trHeapMemory()) TR::RegisterDependencyConditions(0, 1, cg()); - postDeps->addPostCondition(javaReturnRegister, realReg); - - TR::Instruction* end =generateS390LabelInstruction(cg(), TR::InstOpCode::LABEL, callNode, tempLabel); - end->setEndInternalControlFlow(); - end->setDependencyConditions(postDeps); - } - -void -TR::J9S390JNILinkage::callIFAOffloadCheck(TR::Node* callNode, TR_RuntimeHelper helperIndex) - { - TR::SymbolReference * offloadSymRef = cg()->symRefTab()->findOrCreateRuntimeHelper(helperIndex, false, false, false); - TR::Node* helperCallNode = TR::Node::createWithSymRef(TR::call, 1, offloadSymRef); - TR::Node* ramMethodParam = ramMethodParam = TR::Node::create(TR::aconst, 0); - - void* j9methodAsParam = callNode->getSymbol()->castToResolvedMethodSymbol()->getResolvedMethod()->resolvedMethodAddress(); - ramMethodParam->setAddress(reinterpret_cast(j9methodAsParam)); - helperCallNode->setAndIncChild(0, ramMethodParam); - - TR::S390CHelperLinkage* cHelperLinkage = static_cast(cg()->getLinkage(TR_CHelper)); - cHelperLinkage->buildDirectDispatch(helperCallNode, NULL, NULL, true); - } - -/** - * \details - * - * The 5-slot callout frame is structures as the following: - * - * \verbatim - * - * |-----| - * | | <-- constJNICallOutFrameSpecialTag() (For jni thunk, constJNICallOutFrameInvisibleTag()) - * 16/32 |-----| - * | | <-- savedPC. Empty. - * 12/24 |-----| - * | | <-- return address for JNI call - * 8/16 |-----| - * | | <-- constJNICallOutFrameFlags() - * 4/8 ----- - * | | <-- ramMethod for the native method - * ----- <-- stack pointer - * - * \endverbatim - * - * If there's IFA offload check, do no store java stack pointer to vmThread because C helper stores JSP to vmThread, overwritting the JNI's JSP. - * Delay the JSP storing after the IFA call (before the JNI call). - */ -void -TR::J9S390JNILinkage::setupJNICallOutFrameIfNeeded(TR::Node * callNode, - bool isJNICallOutFrame, - bool isJavaOffLoadCheck, - TR::RealRegister * javaStackPointerRealRegister, - TR::Register * methodMetaDataVirtualRegister, - TR::Register* javaLitOffsetReg, - TR::S390JNICallDataSnippet *jniCallDataSnippet, - TR::Register* tempReg) - { - TR_J9VMBase *fej9 = (TR_J9VMBase *)(fe()); - TR::Instruction * cursor = NULL; - - if (isJNICallOutFrame) - { - int32_t stackAdjust = (-5 * static_cast(sizeof(intptrj_t))); - - cursor = generateRXYInstruction(cg(), TR::InstOpCode::LAY, callNode, tempReg, - generateS390MemoryReference(javaStackPointerRealRegister, stackAdjust, cg()), cursor); - - // Check VMThread field order - TR_ASSERT_FATAL((fej9->thisThreadGetJavaFrameFlagsOffset() == fej9->thisThreadGetJavaLiteralsOffset() + TR::Compiler->om.sizeofReferenceAddress()) - && fej9->thisThreadGetJavaLiteralsOffset() == fej9->thisThreadGetJavaPCOffset() + TR::Compiler->om.sizeofReferenceAddress(), - "The vmthread field order should be pc,literals,jitStackFrameFlags\n"); - - // Fill up vmthread structure PC, Literals, and jitStackFrameFlags fields - generateSS1Instruction(cg(), TR::InstOpCode::MVC, callNode, 3*(TR::Compiler->om.sizeofReferenceAddress()) - 1, - generateS390MemoryReference(methodMetaDataVirtualRegister, fej9->thisThreadGetJavaPCOffset(), cg()), - generateS390MemoryReference(jniCallDataSnippet->getBaseRegister(), jniCallDataSnippet->getPCOffset(), cg())); - - // store the JNI's adjusted jsp. - if (!isJavaOffLoadCheck) - { - generateRXInstruction(cg(), TR::InstOpCode::getStoreOpCode(), callNode, tempReg, - generateS390MemoryReference(methodMetaDataVirtualRegister, - fej9->thisThreadGetJavaSPOffset(), cg())); - } - - // Copy 5 slots from the JNI call data snippet to the stack as the callout frame - generateSS1Instruction(cg(), TR::InstOpCode::MVC, callNode, -stackAdjust - 1, - generateS390MemoryReference(tempReg, 0, cg()), - generateS390MemoryReference(jniCallDataSnippet->getBaseRegister(), - jniCallDataSnippet->getJNICallOutFrameDataOffset(), cg())); - } - else - { - // store java stack pointer - if (!isJavaOffLoadCheck) - { - generateRXInstruction(cg(), TR::InstOpCode::getStoreOpCode(), callNode, - javaStackPointerRealRegister, - generateS390MemoryReference(methodMetaDataVirtualRegister, - fej9->thisThreadGetJavaSPOffset(), cg())); - } - - auto* literalOffsetMemoryReference = generateS390MemoryReference(methodMetaDataVirtualRegister, - fej9->thisThreadGetJavaLiteralsOffset(), cg()); - - // Set up literal offset slot to zero - if (cg()->getS390ProcessorInfo()->supportsArch(TR_S390ProcessorInfo::TR_z10)) - { - generateSILInstruction(cg(), TR::InstOpCode::getMoveHalfWordImmOpCode(), callNode, literalOffsetMemoryReference, 0); - } - else - { - generateRIInstruction(cg(), TR::InstOpCode::getLoadHalfWordImmOpCode(), callNode, javaLitOffsetReg, 0); - - generateRXInstruction(cg(), TR::InstOpCode::getStoreOpCode(), callNode, javaLitOffsetReg, literalOffsetMemoryReference); - } - } - } - -TR::S390JNICallDataSnippet* -TR::J9S390JNILinkage::buildJNICallDataSnippet(TR::Node* callNode, - TR::RegisterDependencyConditions* deps, - intptrj_t targetAddress, - bool isJNICallOutFrame, - bool isReleaseVMAccess, - TR::LabelSymbol * returnFromJNICallLabel, - int64_t killMask) - { - if (!(isJNICallOutFrame || isReleaseVMAccess)) - return NULL; - - TR::S390JNICallDataSnippet * jniCallDataSnippet = NULL; - TR_ResolvedMethod * resolvedMethod = callNode->getSymbol()->castToResolvedMethodSymbol()->getResolvedMethod(); - TR_J9VMBase *fej9 = static_cast(fe()); - - TR::Register * JNISnippetBaseReg = NULL; - killMask = killAndAssignRegister(killMask, deps, &JNISnippetBaseReg, TR::RealRegister::GPR10, cg(), true); - jniCallDataSnippet = new (trHeapMemory()) TR::S390JNICallDataSnippet(cg(), callNode); - cg()->addSnippet(jniCallDataSnippet); - jniCallDataSnippet->setBaseRegister(JNISnippetBaseReg); - new (trHeapMemory()) TR::S390RILInstruction(TR::InstOpCode::LARL, callNode, - jniCallDataSnippet->getBaseRegister(), - jniCallDataSnippet, cg()); - - jniCallDataSnippet->setTargetAddress(targetAddress); - - if (isReleaseVMAccess) - { - intptrj_t aValue = fej9->constReleaseVMAccessMask(); //0xfffffffffffdffdf - jniCallDataSnippet->setConstReleaseVMAccessMask(aValue); - - aValue = fej9->constReleaseVMAccessOutOfLineMask(); //0x340001 - jniCallDataSnippet->setConstReleaseVMAccessOutOfLineMask(aValue); - } - - if (isJNICallOutFrame) - { - jniCallDataSnippet->setPC(fej9->constJNICallOutFrameType()); - jniCallDataSnippet->setLiterals(0); - jniCallDataSnippet->setJitStackFrameFlags(0); - - // JNI Callout Frame setup - // 0(sp) : RAM method for the native - jniCallDataSnippet->setRAMMethod(reinterpret_cast(resolvedMethod->resolvedMethodAddress())); - - // 4[8](sp) : flags - jniCallDataSnippet->setJNICallOutFrameFlags(fej9->constJNICallOutFrameFlags()); - - // 8[16](sp) : return address (savedCP) - jniCallDataSnippet->setReturnFromJNICall(returnFromJNICallLabel); - - // 12[24](sp) : savedPC - jniCallDataSnippet->setSavedPC(0); - - // 16[32](sp) : tag bits (savedA0) - intptr_t tagBits = fej9->constJNICallOutFrameSpecialTag(); - // if the current method is simply a wrapper for the JNI call, hide the call-out stack frame - if (resolvedMethod == comp()->getCurrentMethod()) - { - tagBits |= fej9->constJNICallOutFrameInvisibleTag(); - } - - jniCallDataSnippet->setTagBits(tagBits); - } - - return jniCallDataSnippet; - } - -/** - * -*/ -void -TR::J9S390JNILinkage::addEndOfAllJNILabel(TR::Node* callNode, - TR::RegisterDependencyConditions* deps, - TR::Register* javaReturnRegister) - { - uint32_t numPostDeps = deps->getAddCursorForPost(); - int32_t returnRegPos = 0; - - TR::RegisterDependencyConditions * endOfAllJNIDeps = generateRegisterDependencyConditions(0, numPostDeps, cg()); - TR::RealRegister::RegNum returnRealReg = TR::RealRegister::NoReg; - - if (javaReturnRegister != NULL) - { - returnRegPos = deps->searchPostConditionRegisterPos(javaReturnRegister); - returnRealReg = deps->getPostConditions()->getRegisterDependency(returnRegPos)->getRealRegister(); - } - - for(int i = 0; i < numPostDeps; ++i) - { - TR::RegisterDependency* dependency = deps->getPostConditions()->getRegisterDependency(i); - TR::Register* tmpReg = dependency->getRegister(); - TR::RealRegister::RegNum realNum = dependency->getRealRegister(); - TR_RegisterKinds tmpRegKind = tmpReg->getKind(); - - bool isOldTmpReg64bit = tmpReg->is64BitReg(); - uint32_t depFlag = dependency->getFlags(); - - if ((javaReturnRegister != NULL) && (realNum == returnRealReg)) - { - endOfAllJNIDeps->addPostCondition(tmpReg, realNum); - } - else - { - tmpReg = cg()->allocateRegister(tmpRegKind); - tmpReg->setPlaceholderReg(); - - if (isOldTmpReg64bit) - tmpReg->setIs64BitReg(); - - endOfAllJNIDeps->addPostCondition(tmpReg, realNum); - cg()->stopUsingRegister(tmpReg); - } - - endOfAllJNIDeps->getPostConditions()->getRegisterDependency(i)->assignFlags(depFlag); - } - - TR::Instruction* endOfAllJNILabel = generateS390LabelInstruction(cg(), TR::InstOpCode::LABEL, - callNode, generateLabelSymbol(cg())); - endOfAllJNILabel->setDependencyConditions(endOfAllJNIDeps); - } - -/** - * \brief - * A utility function to print the dependency information of JNI VM helper calls. -*/ -void -TR::J9S390JNILinkage::printDepsDebugInfo(TR::Node* callNode, TR::RegisterDependencyConditions* deps) - { - traceMsg(comp(), "CHelper linkage for n%dn %s in %s\n", - callNode->getGlobalIndex(), - callNode->getSymbolReference()->getSymbol()->getName(), - comp()->signature()); - - if (deps != NULL) - { - traceMsg(comp(), "pre helper call registers:: %d %d\n", - (deps)->getAddCursorForPost(), (deps)->getNumPostConditions()); - - for(int i = 0; i< (deps)->getAddCursorForPost(); ++i) - { - TR::Register* tmpReg = (deps)->getPostConditions()->getRegisterDependency(i)->getRegister(); - TR::RealRegister::RegNum realNum =(deps)->getPostConditions()->getRegisterDependency(i)->getRealRegister(); - - traceMsg(comp(), "%s- GPR %d %s, []\n", - tmpReg->getRegisterName(comp()), - realNum-1, - tmpReg->isPlaceholderReg() ? "dummy" : "no-dummy"); - } - } - } - -#ifdef J9ZOS390 -void -TR::J9S390JNILinkage::saveRegistersForCEEHandler(TR::Node * callNode, TR::Register* vmThreadRegister) - { - // CSP save offset is JIT_GPR_SAVE_OFFSET(CSP) defined as: - // define({JIT_GPR_SAVE_OFFSET},{eval(STACK_BIAS+J9TR_cframe_jitGPRs+((CSP)*J9TR_pointerSize))}) - // - // C stack pointer (CSP) is GPR15 on zLinux, and GPR4 on zOS - uint32_t cspNum = 4; - uint32_t stackBias = 2048; - uint32_t cframeJitGPRs = offsetof(J9CInterpreterStackFrame, jitGPRs); - - TR::RealRegister::RegNum sspRegNum = static_cast(cg()->getLinkage(TR_System))->getStackPointerRegister(); - TR::Register* sspReg = getS390RealRegister(sspRegNum); - uint32_t cspSaveBias = stackBias + cframeJitGPRs + cspNum*sizeof(intptrj_t); - - // store GPR4 to vmThread->returnValue - generateRXInstruction(cg(), TR::InstOpCode::getStoreOpCode(), callNode, sspReg, - generateS390MemoryReference(vmThreadRegister, offsetof(J9VMThread, returnValue), cg())); - - // load SSP - generateRXInstruction(cg(), TR::InstOpCode::getLoadOpCode(), callNode, sspReg, - generateS390MemoryReference(vmThreadRegister, offsetof(J9VMThread, systemStackPointer), cg())); - - // store GPR4 to system stack SSP save slot - // CEEHDLR will hang if the SSP is not in the returnValue field of J9VMThread - generateSS1Instruction(cg(), TR::InstOpCode::MVC, callNode, sizeof(intptrj_t) - 1, - generateS390MemoryReference(sspReg, cspSaveBias, cg()), - generateS390MemoryReference(vmThreadRegister, offsetof(J9VMThread, returnValue), cg())); - - // The JNI definitly need GPR13 on the system stack for CEEHDLR. - // Otherwise, the GPR13 will be 0 after JNI call returns. This leads to crashes in the JIT'ed code. - uint32_t gpr13SaveOffset = stackBias + cframeJitGPRs + 13*sizeof(intptrj_t); - generateRXInstruction(cg(), TR::InstOpCode::getStoreOpCode(), callNode, vmThreadRegister, - generateS390MemoryReference(sspReg, gpr13SaveOffset, cg())); - } -#endif - -/** - * - * \details - * - * The JNI dispatch sequence is: - * - * -# Setup register dependency for the JNI linkage and build native call arguments by - * passing arguments from the Java side to the native side according to the system - * linkage (e.g. XPLINK on z/OS) - * -# If a callout frame is needed, build the 5-slot callout frame on top of the Java stack. - * -# Call releaseVMAccess VM helper to prepare for JNI entry. - * -# [z/OS only] Invoke IFA switching VM helper to turn off IFA if the native code is - * not zIIP-eligible. In doing so, native code execution is switched from zIIPs to GCPs. - * -# Branch to native code. - * -# Upon return from the native code, restore the Java stack pointer by - * --# loading the Java stack pointer from the VMThread structure. - * --# adjusting the newly loaded Java stack pointer by adding the Java literal offset it. - * The literal offset is the number of object references used by the native code - * -# If a callout frame was built prior to the native call, collapse the 5-slot callout - * frame by simply shrinking the Java stack pointer by 5 * sizeof(intptrj_t). - * -# Build an End-JNI-Post-dependencies. - * -# [z/OS only] Invoke IFA switching VM helper to turn on IFA if the native - * code is not zIIP-eligible. - * -# Call acquireVMAccessMask to get VM access and prevent GC from happening. - * -# If the native code returns a wrapped object, unwrap the return value. - * -# Collapse the JNI reference frame by calling the TR_S390collapseJNIReferenceFrame VM - * helper if needed - * -# Check exceptions by calling the check exception VM helper if needed - * -# Add an end-of-all-JNI post-dependency group. - * - * Special notes: - * -# The last end-of-all-JNI post-dependency group is needed because JNI does not preserve any objects - * in any registers. Reference stored in any register is invalidated because GC can't - * see or update them when the native code is being executed. This kills-all - * dependency group is placed at the very end (after all helpers) because some - * helpers (IFA offload, for instance) are c-helper calls with dependencies. These CHelpers - * can cause spills and reverse spills around them. But the Java stack pointer is an adjusted pointer - * that can not be used for spills. Hence, placing a kills-all dependency group to force - * reverse-spills happen at the end of all JNI related operations. - * -# There are two temporary registers used across the whole JNI dispatch: GPR11/GPR9 on z/OS - * and GPR1/GPR9 on zLinux. For example, GPR11 and GPR9 are used to for VM access release - * and acquire around the JNI call. - * -# Certain native functions (e.g. java/lang/Math) are marked as `canDirectNativeCall` and are void - * of many VM helper calls (overhead). These include releasing/acquiring VM access, checking exceptions - * upon return, building and tearing down of the JNI callout frame, and collapsing of the JNI reference frame. -*/ -TR::Register* -TR::J9S390JNILinkage::buildDirectDispatch(TR::Node * callNode) - { - if (comp()->getOption(TR_TraceCG)) - traceMsg(comp(), "\nBuild JNI Direct Dispatch\n"); - - TR_J9VMBase *fej9 = static_cast(fe()); - - TR::SystemLinkage * systemLinkage = static_cast(cg()->getLinkage(TR_System)); - TR::LabelSymbol * returnFromJNICallLabel = generateLabelSymbol(cg()); - int32_t numDeps = systemLinkage->getNumberOfDependencyGPRegisters(); - if (cg()->supportsHighWordFacility() && !comp()->getOption(TR_DisableHighWordRA)) - numDeps += 16; //HPRs need to be spilled - if (cg()->getSupportsVectorRegisters()) - numDeps += 32; //VRFs need to be spilled - - // 70896 Remove DEPEND instruction and merge glRegDeps to call deps - // *Speculatively* increase numDeps for dependencies from glRegDeps - // which is added right before callNativeFunction. - // GlobalRegDeps should not add any more children after here. - // TR::RegisterDependencyConditions *glRegDeps; - TR::Node *GlobalRegDeps; - - bool hasGlRegDeps = (callNode->getNumChildren() >= 1) && - (callNode->getChild(callNode->getNumChildren()-1)->getOpCodeValue() == TR::GlRegDeps); - if(hasGlRegDeps) - { - GlobalRegDeps = callNode->getChild(callNode->getNumChildren()-1); - numDeps += GlobalRegDeps->getNumChildren(); - } - - TR::RegisterDependencyConditions * deps = generateRegisterDependencyConditions(numDeps, numDeps, cg()); - int64_t killMask = -1; - TR::RealRegister * javaStackPointerRealRegister = getStackPointerRealRegister(); - TR::RealRegister * methodMetaDataRealRegister = getMethodMetaDataRealRegister(); - TR::RealRegister * javaLitPoolRealRegister = getLitPoolRealRegister(); - TR::Register * methodMetaDataVirtualRegister = methodMetaDataRealRegister; - - TR::Register * methodAddressReg = NULL; - TR::Register * javaLitOffsetReg = NULL; - comp()->setHasNativeCall(); - - if (cg()->getSupportsRuntimeInstrumentation()) - TR::TreeEvaluator::generateRuntimeInstrumentationOnOffSequence(cg(), TR::InstOpCode::RIOFF, callNode); - - TR::ResolvedMethodSymbol * cs = callNode->getSymbol()->castToResolvedMethodSymbol(); - TR_ResolvedMethod * resolvedMethod = cs->getResolvedMethod(); - - bool isPassJNIThread = !fej9->jniDoNotPassThread(resolvedMethod); - bool isPassReceiver = !fej9->jniDoNotPassReceiver(resolvedMethod); - bool isJNIGCPoint = !fej9->jniNoGCPoint(resolvedMethod); - bool isJNICallOutFrame = !fej9->jniNoNativeMethodFrame(resolvedMethod); - bool isReleaseVMAccess = !fej9->jniRetainVMAccess(resolvedMethod); - bool isAcquireVMAccess = isReleaseVMAccess; - bool isCollapseJNIReferenceFrame = !fej9->jniNoSpecialTeardown(resolvedMethod); - bool isCheckException = !fej9->jniNoExceptionsThrown(resolvedMethod); - bool isUnwrapAddressReturnValue = !fej9->jniDoNotWrapObjects(resolvedMethod); - bool isJavaOffLoadCheck = false; - bool hasCEEHandlerAndFrame = fej9->CEEHDLREnabled() && isJNICallOutFrame; - - killMask = killAndAssignRegister(killMask, deps, &methodAddressReg, (TR::Compiler->target.isLinux()) ? - TR::RealRegister::GPR1 : TR::RealRegister::GPR9 , cg(), true); - - killMask = killAndAssignRegister(killMask, deps, &javaLitOffsetReg, TR::RealRegister::GPR11, cg(), true); - - intptrj_t targetAddress = reinterpret_cast(resolvedMethod->startAddressForJNIMethod(comp())); - TR::DataType returnType = resolvedMethod->returnType(); - - static char * disablePureFn = feGetEnv("TR_DISABLE_PURE_FUNC_RECOGNITION"); - if (cs->canDirectNativeCall()) - { - isReleaseVMAccess = false; - isAcquireVMAccess = false; - isJNIGCPoint = false; - isCheckException = false; - isJNICallOutFrame = false; - isCollapseJNIReferenceFrame = false; - } - - if (cs->isPureFunction() && (disablePureFn == NULL)) - { - isReleaseVMAccess = false; - isAcquireVMAccess = false; - isCheckException = false; - } - - static bool forceJNIOffloadCheck = feGetEnv("TR_force_jni_offload_check") != NULL; - bool methodIsNotzIIPable = static_cast(resolvedMethod)->methodIsNotzAAPEligible(); - if ((fej9->isJavaOffloadEnabled() && methodIsNotzIIPable) - || (fej9->CEEHDLREnabled() && isJNICallOutFrame) - || (forceJNIOffloadCheck && methodIsNotzIIPable && TR::Compiler->target.isZOS())) - isJavaOffLoadCheck = true; - - if (comp()->getOption(TR_TraceCG)) - traceMsg(comp(), "isPassReceiver: %d, isOffloadCheck %d, isPassJNIThread: %d, isCEEHDLREnabled %d, isJNIGCPoint: %d, isJNICallOutFrame:%d, isReleaseVMAccess: %d, isCollapseJNIReferenceFrame: %d, methodNOTzIIPable: %d\n", - isPassReceiver, isJavaOffLoadCheck, isPassJNIThread, - (fej9->CEEHDLREnabled() && isJNICallOutFrame), - isJNIGCPoint, - isJNICallOutFrame, isReleaseVMAccess, - isCollapseJNIReferenceFrame, methodIsNotzIIPable); - - if (isPassJNIThread) - { - //First param for JNI call in JNIEnv pointer - TR::Register * jniEnvRegister = cg()->allocateRegister(); - deps->addPreCondition(jniEnvRegister, systemLinkage->getIntegerArgumentRegister(0)); - generateRRInstruction(cg(), TR::InstOpCode::getLoadRegOpCode(), callNode, - jniEnvRegister, methodMetaDataVirtualRegister); - } - - // JNI dispatch does not allow for any object references to survive in preserved registers - // they are saved onto the system stack, which the stack walker has no way of accessing. - // Hence, ensure we kill all preserved HPRs (6-12) as well - if (cg()->supportsHighWordFacility() && !comp()->getOption(TR_DisableHighWordRA)) - { - TR::Register *dummyReg = NULL; - killAndAssignRegister(killMask, deps, &dummyReg, REGNUM(TR::RealRegister::HPR6), cg(), true, true ); - killAndAssignRegister(killMask, deps, &dummyReg, REGNUM(TR::RealRegister::HPR7), cg(), true, true ); - killAndAssignRegister(killMask, deps, &dummyReg, REGNUM(TR::RealRegister::HPR8), cg(), true, true ); - killAndAssignRegister(killMask, deps, &dummyReg, REGNUM(TR::RealRegister::HPR9), cg(), true, true ); - killAndAssignRegister(killMask, deps, &dummyReg, REGNUM(TR::RealRegister::HPR10), cg(), true, true ); - killAndAssignRegister(killMask, deps, &dummyReg, REGNUM(TR::RealRegister::HPR11), cg(), true, true ); - killAndAssignRegister(killMask, deps, &dummyReg, REGNUM(TR::RealRegister::HPR12), cg(), true, true ); - } - - // Setup native call arguments - TR::S390PrivateLinkage* privateLinkage = static_cast(cg()->getLinkage(TR_Private)); - privateLinkage->setupRegisterDepForLinkage(callNode, TR_JNIDispatch, deps, killMask, systemLinkage, GlobalRegDeps, hasGlRegDeps, &methodAddressReg, javaLitOffsetReg); - OMR::Z::Linkage::setupBuildArgForLinkage(callNode, TR_JNIDispatch, deps, true, isPassReceiver, killMask, GlobalRegDeps, hasGlRegDeps, systemLinkage); - - // Create the S390JNICallDataSnippet metadata - // Information in this snippet is hardcoded in the JIT'ed function body as data. - TR::S390JNICallDataSnippet * jniCallDataSnippet = buildJNICallDataSnippet(callNode, deps, - targetAddress, - isJNICallOutFrame, isReleaseVMAccess, - returnFromJNICallLabel, - killMask); - // Sets up PC, Stack pointer and literals offset slots. - setupJNICallOutFrameIfNeeded(callNode, isJNICallOutFrame, isJavaOffLoadCheck, - javaStackPointerRealRegister, methodMetaDataVirtualRegister, - javaLitOffsetReg, - jniCallDataSnippet, methodAddressReg); - - if (isReleaseVMAccess) - { -#ifdef J9VM_INTERP_ATOMIC_FREE_JNI - releaseVMAccessMaskAtomicFree(callNode, methodMetaDataVirtualRegister, methodAddressReg); -#else - releaseVMAccessMask(callNode, methodMetaDataVirtualRegister, methodAddressReg, javaLitOffsetReg, jniCallDataSnippet, deps); -#endif - } - -#ifdef J9ZOS390 - if (fej9->CEEHDLREnabled()) - saveRegistersForCEEHandler(callNode, methodMetaDataVirtualRegister); -#endif - - TR::Instruction* cursor = NULL; - - // Turn off Java Offload if calling user native - // Depends on calloutframe setup - if (isJavaOffLoadCheck) - { - callIFAOffloadCheck(callNode, TR_S390jitPreJNICallOffloadCheck); - - // Fix up jsp in the VMThread. - // The IFA offload C-Helper call requires an un-adjusted java stack so that spills and reverse - // spills are done correctly. - // At the end of the IFA offload call, the un-adjusted java stack will be loaded. - // Therefore, we need to adjust it and store the updated java stack to vmthread. - if (isJNICallOutFrame) - { - int32_t stackAdjust = (-5 * static_cast(sizeof(intptrj_t))); - - cursor = generateRXYInstruction(cg(), TR::InstOpCode::LAY, callNode, javaStackPointerRealRegister, - generateS390MemoryReference(javaStackPointerRealRegister, stackAdjust, cg()), cursor); - - // store out jsp - generateRXInstruction(cg(), TR::InstOpCode::getStoreOpCode(), callNode, javaStackPointerRealRegister, - generateS390MemoryReference(methodMetaDataVirtualRegister, - fej9->thisThreadGetJavaSPOffset(), cg())); - } - } - - // Generate a call to the native function - TR::Register* javaReturnRegister = systemLinkage->callNativeFunction(callNode, - deps, targetAddress, methodAddressReg, - javaLitOffsetReg, returnFromJNICallLabel, - jniCallDataSnippet, isJNIGCPoint); - - // restore java stack pointer - cursor = generateRXInstruction(cg(), TR::InstOpCode::getLoadOpCode(), callNode, - javaStackPointerRealRegister, - generateS390MemoryReference(methodMetaDataVirtualRegister, - fej9->thisThreadGetJavaSPOffset(), cg())); - - cursor = generateRXInstruction(cg(), TR::InstOpCode::getAddOpCode(), callNode, - javaStackPointerRealRegister, - generateS390MemoryReference(methodMetaDataVirtualRegister, - fej9->thisThreadGetJavaLiteralsOffset(), cg())); - - // Pop the JNI callout frame - if (isJNICallOutFrame) - { - cursor = generateRXInstruction(cg(), TR::InstOpCode::LA, callNode, - javaStackPointerRealRegister, - generateS390MemoryReference(javaStackPointerRealRegister, JNI_CALLOUT_FRAME_SIZE, cg())); - } - - //OMR::Z::Linkage::generateDispatchReturnLable(callNode, cg(), deps, javaReturnRegister, hasGlRegDeps, GlobalRegDeps); - TR::RegisterDependencyConditions * endJNIPostDeps = new (self()->trHeapMemory()) - TR::RegisterDependencyConditions(NULL, deps->getPostConditions(), 0, deps->getAddCursorForPost(), cg()); - - cursor->setDependencyConditions(endJNIPostDeps); - - if (cg()->getSupportsRuntimeInstrumentation()) - TR::TreeEvaluator::generateRuntimeInstrumentationOnOffSequence(cg(), TR::InstOpCode::RION, callNode); - // ========================================== END OF JNI. Helper calls below ======================================== - - //Turn on Java Offload - if (isJavaOffLoadCheck) - callIFAOffloadCheck(callNode, TR_S390jitPostJNICallOffloadCheck); - - if (isAcquireVMAccess) - { -#ifdef J9VM_INTERP_ATOMIC_FREE_JNI - acquireVMAccessMaskAtomicFree(callNode, methodMetaDataVirtualRegister, methodAddressReg); -#else - acquireVMAccessMask(callNode, javaLitPoolRealRegister, methodMetaDataVirtualRegister); -#endif - } - - isUnwrapAddressReturnValue = (isUnwrapAddressReturnValue && - (returnType == TR::Address) && - (javaReturnRegister!= NULL) && - (javaReturnRegister->getKind() == TR_GPR)); - - if (isUnwrapAddressReturnValue) - { - unwrapReturnValue(callNode, javaReturnRegister, deps); - } - - if (isCollapseJNIReferenceFrame) - { - collapseJNIReferenceFrame(callNode, javaLitPoolRealRegister, methodAddressReg, javaStackPointerRealRegister); - } - - if (isCheckException) - { - checkException(callNode, javaLitOffsetReg, methodMetaDataVirtualRegister); - } - - // ================================== Stop using JNI post deps registers ============================================= - - callNode->setRegister(javaReturnRegister); - - if (javaReturnRegister) - { - deps->stopUsingDepRegs(cg(), javaReturnRegister->getRegisterPair() == NULL ? javaReturnRegister : - javaReturnRegister->getHighOrder(), javaReturnRegister->getLowOrder()); - } - else - { - deps->stopUsingDepRegs(cg()); - } - - - if(hasGlRegDeps) - { - cg()->decReferenceCount(GlobalRegDeps); - } - - // ================================== END of the entire JNI sequence. Kill all regs ======================================== - - if (cg()->getDebug() && comp()->getOption(TR_TraceCG)) - printDepsDebugInfo(callNode, deps); - - addEndOfAllJNILabel(callNode, deps, javaReturnRegister); - - return javaReturnRegister; - } - - -#ifdef J9VM_INTERP_ATOMIC_FREE_JNI - -/** - * \details - * This is the atomic-free JNI design and works in conjunction with VMAccess.cpp - * atomic-free JNI changes. - * - * In the JNI dispatch sequence, a release-vm-access action is performed before - * the branch to native code; and an acquire-vm-access - * is done after the thread execution returns from the native call. Both of the - * actions require synchronization between the - * application thread and the GC thread. This was previously implemented with the - * atomic compare-and-swap (CS) instruction, which is slow in nature. - * - * To speed up the JNI acquire and release access actions (the fast path), a - * store-load sequence is generated by this evaluator - * to replace the CS instruction. Normally, the fast path ST-LD are not serialized - * and can be done out-of-order for higher performance. Synchronization - * burden is offloaded to the slow path. - * - * The slow path is where a thread tries to acquire exclusive vm access. The slow path - * should be taken proportionally less often than the fast - * path. Should the slow path be taken, that thread will be penalized by calling a slow - * flushProcessWriteBuffer() routine so that all threads - * can momentarily synchronize memory writes. Having fast and slow paths makes the - * atomic-free JNI design asymmetric. - * - * z/OS notes - * zOS currently does not support the asymmetric algorithm. Hence, - * a serialization instruction (BCR 14,0) between the store and the load. - */ -void -TR::J9S390JNILinkage::releaseVMAccessMaskAtomicFree(TR::Node * callNode, - TR::Register * methodMetaDataVirtualRegister, - TR::Register * tempReg1) - { - TR_J9VMBase *fej9 = static_cast(fe()); - TR::CodeGenerator* cg = self()->cg(); - TR::Compilation* comp = self()->comp(); - - // Store a 1 into vmthread->inNative - generateSILInstruction(cg, TR::InstOpCode::getMoveHalfWordImmOpCode(), callNode, - generateS390MemoryReference(methodMetaDataVirtualRegister, offsetof(J9VMThread, inNative), cg), - 1); - -#if !defined(J9VM_INTERP_ATOMIC_FREE_JNI_USES_FLUSH) - generateSerializationInstruction(cg, callNode, NULL); -#endif - - // Compare vmthread public flag with J9_PUBLIC_FLAGS_VM_ACCESS - generateRXInstruction(cg, TR::InstOpCode::getLoadOpCode(), callNode, tempReg1, - generateS390MemoryReference(methodMetaDataVirtualRegister, fej9->thisThreadGetPublicFlagsOffset(), cg)); - - TR::LabelSymbol * longReleaseSnippetLabel = generateLabelSymbol(cg); - TR::LabelSymbol * longReleaseRestartLabel = generateLabelSymbol(cg); - - TR_ASSERT_FATAL(J9_PUBLIC_FLAGS_VM_ACCESS >= MIN_IMMEDIATE_BYTE_VAL && J9_PUBLIC_FLAGS_VM_ACCESS <= MAX_IMMEDIATE_BYTE_VAL, "VM access bit must be immediate"); - generateRIEInstruction(cg, TR::InstOpCode::getCmpImmBranchRelOpCode(), callNode, tempReg1, J9_PUBLIC_FLAGS_VM_ACCESS, longReleaseSnippetLabel, TR::InstOpCode::COND_BNE); - cg->addSnippet(new (self()->trHeapMemory()) TR::S390HelperCallSnippet(cg, - callNode, longReleaseSnippetLabel, - comp->getSymRefTab()->findOrCreateAcquireVMAccessSymbolRef(comp->getJittedMethodSymbol()), - longReleaseRestartLabel)); - - generateS390LabelInstruction(cg, TR::InstOpCode::LABEL, callNode, longReleaseRestartLabel); - } - -/** - * \brief - * Build the atomic-free acquire VM access sequence for JNI dispatch. - * */ -void -TR::J9S390JNILinkage::acquireVMAccessMaskAtomicFree(TR::Node * callNode, - TR::Register * methodMetaDataVirtualRegister, - TR::Register * tempReg1) - { - TR_J9VMBase *fej9 = static_cast(fe()); - TR::CodeGenerator* cg = self()->cg(); - TR::Compilation* comp = self()->comp(); - - // Zero vmthread->inNative, which is a UDATA field - generateSS1Instruction(cg, TR::InstOpCode::XC, callNode, TR::Compiler->om.sizeofReferenceAddress() - 1, - generateS390MemoryReference(methodMetaDataVirtualRegister, offsetof(J9VMThread, inNative), cg), - generateS390MemoryReference(methodMetaDataVirtualRegister, offsetof(J9VMThread, inNative), cg)); - -#if !defined(J9VM_INTERP_ATOMIC_FREE_JNI_USES_FLUSH) - generateSerializationInstruction(cg, callNode, NULL); -#endif - - // Compare vmthread public flag with J9_PUBLIC_FLAGS_VM_ACCESS - generateRXInstruction(cg, TR::InstOpCode::getLoadOpCode(), callNode, tempReg1, - generateS390MemoryReference(methodMetaDataVirtualRegister, fej9->thisThreadGetPublicFlagsOffset(), cg)); - - TR::LabelSymbol * longAcquireSnippetLabel = generateLabelSymbol(cg); - TR::LabelSymbol * longAcquireRestartLabel = generateLabelSymbol(cg); - - TR_ASSERT_FATAL(J9_PUBLIC_FLAGS_VM_ACCESS >= MIN_IMMEDIATE_BYTE_VAL && J9_PUBLIC_FLAGS_VM_ACCESS <= MAX_IMMEDIATE_BYTE_VAL, "VM access bit must be immediate"); - generateRIEInstruction(cg, TR::InstOpCode::getCmpImmBranchRelOpCode(), callNode, tempReg1, J9_PUBLIC_FLAGS_VM_ACCESS, longAcquireSnippetLabel, TR::InstOpCode::COND_BNE); - - cg->addSnippet(new (self()->trHeapMemory()) TR::S390HelperCallSnippet(cg, - callNode, longAcquireSnippetLabel, - comp->getSymRefTab()->findOrCreateAcquireVMAccessSymbolRef(comp->getJittedMethodSymbol()), - longAcquireRestartLabel)); - - generateS390LabelInstruction(cg, TR::InstOpCode::LABEL, callNode, longAcquireRestartLabel); - } -#else - -/** - * \brief - * Release vm access. Invoked before transitioning from the JIT'ed code to native code. - * - * At this point: arguments for the native routine are all in place already, i.e., if there are - * more than 24 byte worth of arguments, some of them are on the stack. However, - * we potentially go out to call a helper before jumping to the native. - * but the helper call saves and restores all regs - */ -void -TR::J9S390JNILinkage::releaseVMAccessMask(TR::Node * callNode, - TR::Register * methodMetaDataVirtualRegister, - TR::Register * methodAddressReg, TR::Register * javaLitOffsetReg, - TR::S390JNICallDataSnippet * jniCallDataSnippet, - TR::RegisterDependencyConditions * deps) - { - TR::LabelSymbol * loopHead = generateLabelSymbol(self()->cg()); - TR::LabelSymbol * longReleaseSnippetLabel = generateLabelSymbol(self()->cg()); - TR::LabelSymbol * doneLabel = generateLabelSymbol(self()->cg()); - TR_J9VMBase *fej9 = (TR_J9VMBase *)(self()->fe()); - - generateRXInstruction(self()->cg(), TR::InstOpCode::getLoadOpCode(), callNode, methodAddressReg, - generateS390MemoryReference(methodMetaDataVirtualRegister, - fej9->thisThreadGetPublicFlagsOffset(), self()->cg())); - - TR::Instruction * label = generateS390LabelInstruction(self()->cg(), TR::InstOpCode::LABEL, callNode, loopHead); - label->setStartInternalControlFlow(); - - generateRRInstruction(self()->cg(), TR::InstOpCode::getLoadRegOpCode(), callNode, javaLitOffsetReg, methodAddressReg); - generateRXInstruction(self()->cg(), TR::InstOpCode::getAndOpCode(), callNode, javaLitOffsetReg, - generateS390MemoryReference(jniCallDataSnippet->getBaseRegister(), jniCallDataSnippet->getConstReleaseVMAccessOutOfLineMaskOffset(), self()->cg())); - - TR::Instruction * gcPoint = (TR::Instruction *) generateS390BranchInstruction( - self()->cg(), TR::InstOpCode::BRC, TR::InstOpCode::COND_BNE, callNode, longReleaseSnippetLabel); - gcPoint->setNeedsGCMap(0); - - generateRRInstruction(self()->cg(), TR::InstOpCode::getLoadRegOpCode(), callNode, javaLitOffsetReg, methodAddressReg); - generateRXInstruction(self()->cg(), TR::InstOpCode::getAndOpCode(), callNode, javaLitOffsetReg, - generateS390MemoryReference(jniCallDataSnippet->getBaseRegister(), jniCallDataSnippet->getConstReleaseVMAccessMaskOffset(), self()->cg())); - generateRSInstruction(self()->cg(), TR::InstOpCode::getCmpAndSwapOpCode(), callNode, methodAddressReg, javaLitOffsetReg, - generateS390MemoryReference(methodMetaDataVirtualRegister, - fej9->thisThreadGetPublicFlagsOffset(), self()->cg())); - - - //get existing post conditions on the registers parameters and create a new post cond for the internal control flow - TR::RegisterDependencyConditions * postDeps = new (self()->trHeapMemory()) TR::RegisterDependencyConditions(0, 3, self()->cg()); - TR::RealRegister::RegNum realReg; - int32_t regPos = deps->searchPostConditionRegisterPos(methodMetaDataVirtualRegister); - if (regPos >= 0) - { - realReg = deps->getPostConditions()->getRegisterDependency(regPos)->getRealRegister(); - postDeps->addPostCondition(methodMetaDataVirtualRegister, realReg); - } - else - postDeps->addPostCondition(methodMetaDataVirtualRegister, TR::RealRegister::AssignAny); - - regPos = deps->searchPostConditionRegisterPos(methodAddressReg); - if (regPos >= 0) - { - realReg = deps->getPostConditions()->getRegisterDependency(regPos)->getRealRegister(); - postDeps->addPostCondition(methodAddressReg, realReg); - } - else - postDeps->addPostCondition(methodAddressReg, TR::RealRegister::AssignAny); - - regPos = deps->searchPostConditionRegisterPos(javaLitOffsetReg); - if (regPos >= 0) - { - realReg = deps->getPostConditions()->getRegisterDependency(regPos)->getRealRegister(); - postDeps->addPostCondition(javaLitOffsetReg, realReg); - } - else - postDeps->addPostCondition(javaLitOffsetReg, TR::RealRegister::AssignAny); - - - TR::Instruction * br = generateS390BranchInstruction(self()->cg(), TR::InstOpCode::BRC, TR::InstOpCode::COND_BNE, callNode, loopHead); - br->setEndInternalControlFlow(); - br->setDependencyConditions(postDeps); - - generateS390LabelInstruction(self()->cg(), TR::InstOpCode::LABEL, callNode, doneLabel); - - - self()->cg()->addSnippet(new (self()->trHeapMemory()) TR::S390HelperCallSnippet(self()->cg(), callNode, longReleaseSnippetLabel, - self()->comp()->getSymRefTab()->findOrCreateReleaseVMAccessSymbolRef(self()->comp()->getJittedMethodSymbol()), doneLabel)); - // end of release vm access (spin lock) - } - -/** - * -*/ -void TR::J9S390JNILinkage::acquireVMAccessMask(TR::Node * callNode, - TR::Register * javaLitPoolVirtualRegister, - TR::Register * methodMetaDataVirtualRegister) - { - // start of acquire vm access - - TR::Register * tempReg1 = cg()->allocateRegister(); - TR::Register * tempReg2 = cg()->allocateRegister(); - - TR_J9VMBase *fej9 = (TR_J9VMBase *)(self()->fe()); - intptrj_t aValue = fej9->constAcquireVMAccessOutOfLineMask(); - - TR::Instruction * loadInstr = static_cast(genLoadAddressConstant(self()->cg(), - callNode, aValue, tempReg1, - NULL, NULL, javaLitPoolVirtualRegister)); - - switch (loadInstr->getKind()) - { - case TR::Instruction::IsRX: - case TR::Instruction::IsRXE: - case TR::Instruction::IsRXY: - case TR::Instruction::IsRXYb: - ((TR::S390RXInstruction *)loadInstr)->getMemoryReference()->setMemRefMustNotSpill(); - break; - default: - break; - } - - generateRRInstruction(self()->cg(), TR::InstOpCode::getXORRegOpCode(), callNode, tempReg2, tempReg2); - - TR::LabelSymbol * longAcquireSnippetLabel = generateLabelSymbol(self()->cg()); - TR::LabelSymbol * acquireDoneLabel = generateLabelSymbol(self()->cg()); - generateRSInstruction(cg(), TR::InstOpCode::getCmpAndSwapOpCode(), callNode, tempReg2, tempReg1, - generateS390MemoryReference(methodMetaDataVirtualRegister, - (int32_t)fej9->thisThreadGetPublicFlagsOffset(), self()->cg())); - TR::Instruction *gcPoint = (TR::Instruction *) generateS390BranchInstruction(self()->cg(), TR::InstOpCode::BRC, TR::InstOpCode::COND_BNE, callNode, longAcquireSnippetLabel); - gcPoint->setNeedsGCMap(0); - - self()->cg()->addSnippet(new (self()->trHeapMemory()) TR::S390HelperCallSnippet(self()->cg(), callNode, longAcquireSnippetLabel, - self()->comp()->getSymRefTab()->findOrCreateAcquireVMAccessSymbolRef(self()->comp()->getJittedMethodSymbol()), acquireDoneLabel)); - generateS390LabelInstruction(self()->cg(), TR::InstOpCode::LABEL, callNode, acquireDoneLabel); - - cg()->stopUsingRegister(tempReg1); - cg()->stopUsingRegister(tempReg2); - } - -#endif - diff --git a/runtime/compiler/z/codegen/J9S390JNILinkage.hpp b/runtime/compiler/z/codegen/J9S390JNILinkage.hpp deleted file mode 100644 index 88d3b3db353..00000000000 --- a/runtime/compiler/z/codegen/J9S390JNILinkage.hpp +++ /dev/null @@ -1,196 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2018, 2018 IBM Corp. and others - * - * This program and the accompanying materials are made available under - * the terms of the Eclipse Public License 2.0 which accompanies this - * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ - * or the Apache License, Version 2.0 which accompanies this distribution and - * is available at https://www.apache.org/licenses/LICENSE-2.0. - * - * This Source Code may also be made available under the following - * Secondary Licenses when the conditions for such availability set - * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU - * General Public License, version 2 with the GNU Classpath - * Exception [1] and GNU General Public License, version 2 with the - * OpenJDK Assembly Exception [2]. - * - * [1] https://www.gnu.org/software/classpath/license.html - * [2] http://openjdk.java.net/legal/assembly-exception.html - * - * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception - *******************************************************************************/ - -#ifndef J9S390JNILINKAGE_INCL -#define J9S390JNILINKAGE_INCL - -#include "z/codegen/J9S390PrivateLinkage.hpp" - -namespace TR { class CodeGenerator; } -namespace TR { class RegisterDependencyConditions; } -namespace TR { class Snippet; } - -namespace TR { - -/** - * \brief - * The JNI dispatch building class for the z system. - * This linkage class uses the OMR::Z::Linkage to build arguments, TR::S390PrivateLinkage to build dependencies, and - * system linkage to buld the native call. -*/ -class J9S390JNILinkage : public TR::S390PrivateLinkage - { -public: - - J9S390JNILinkage(TR::CodeGenerator * cg, - TR_S390LinkageConventions elc = TR_JavaPrivate, - TR_LinkageConventions lc = TR_J9JNILinkage); - - /** - * \brief - * Build the whole JNI direct dispatch sequence for the JNI call node. - * - */ - virtual TR::Register * buildDirectDispatch(TR::Node * callNode); - -private: - - /** - * \brief - * Print register assignment information for a given dependency group - */ - void printDepsDebugInfo(TR::Node* callNode, TR::RegisterDependencyConditions* deps); - - /** - * \brief - * A pre and post native helper. This function uses the J9S390CHelperLinkage to build a C-helper - * call to invoke IFA switching VM helpers. This is used - * to build calls to turn on and off IFA switching on z/OS. - * - * \param deps - * the JNI call pre and post dependency group - * \param isJNICallOutFrame - * boolean flag to indicated if a callout frame is needed. - * \param helperIndex - * The IFA call helper index - */ - void callIFAOffloadCheck(TR::Node* callNode, TR_RuntimeHelper helperIndex); - - /** - * \brief - * Post-JNI dependency bulider. It appends a post-dependency group at the very end of all - * JNI related operations to kill all GPRs and HPRs. - * This is used to keep RA from spilling and reverse-spilling before the JNI call itself and its helper calls - * are able to finish. - */ - void addEndOfAllJNILabel(TR::Node* callNode, - TR::RegisterDependencyConditions* deps, - TR::Register* javaReturnRegister); - - /** - * \brief - * Pre-JNI frame builder. - * Sets up the 5-slot JNI Callout frame to be used by the VM. - */ - void setupJNICallOutFrameIfNeeded(TR::Node * callNode, - bool isJNICallOutFrame, - bool isJavaOffLoadCheck, - TR::RealRegister * javaStackPointerRealRegister, - TR::Register * methodMetaDataVirtualRegister, - TR::Register* javaLitOffsetReg, - TR::S390JNICallDataSnippet *jniCallDataSnippet, - TR::Register* tempReg); - - /** - * \brief - * A helper function to populate JNI call data and builds up the snippet object. - */ - TR::S390JNICallDataSnippet* - buildJNICallDataSnippet(TR::Node* callNode, - TR::RegisterDependencyConditions* deps, - intptrj_t targetAddress, - bool isJNICallOutFrame, - bool isReleaseVMAccess, - TR::LabelSymbol * returnFromJNICallLabel, - int64_t killMask); - /** - * \brief - * Post-JNI VM helper call builder. - * This builds a call to invoke the VM check exception helper. Used after returning from the native code to check - * native code exceptions. - */ - void checkException(TR::Node * callNode, TR::Register* flagReg, TR::Register* methodMetaDataVirtualRegister); - -#ifdef J9ZOS390 - /** - * \brief - * z/OS only. The CEE handler requires the system stack pointer register and the vmThread register saved on the system stack. - */ - void saveRegistersForCEEHandler(TR::Node * callNode, TR::Register* vmThreadRegister); -#endif - - /** - * \brief - * Post-JNI helper builder. Builds a call to invoke the collapseJNIReference VM helper after the native code returns. - * - * \param callNode - * the JNI call node - * \param flagReg - * a real register that contains the value that determines if a collapse frame call is needed - */ - void collapseJNIReferenceFrame(TR::Node * callNode, - TR::Register* javaLitPoolRealRegister, - TR::Register* methodAddressReg, - TR::Register* javaStackPointerRealRegister); - - - /** - * \brief - * - * - * - */ - void unwrapReturnValue(TR::Node* callNode, - TR::Register* javaReturnRegister, - TR::RegisterDependencyConditions* deps); - -#ifdef J9VM_INTERP_ATOMIC_FREE_JNI - /** - * \brief - * Build the release VM access sequence for JNI dispatch without using the atomic compare-and-swap - * instruction. This is mutually exclusive with the old compare-and-swap releaseVMAccessMask(). - */ - void releaseVMAccessMaskAtomicFree(TR::Node * callNode, - TR::Register * methodMetaDataVirtualRegister, - TR::Register * tempReg1); - - /** - * \brief - * Build the acquire VM access sequence for JNI dispatch without using the atomic compare-and-swap - * instruction. This is mutually exclusive with the old compare-and-swap acquireVMAccessMask(). - */ - void acquireVMAccessMaskAtomicFree(TR::Node * callNode, - TR::Register * methodMetaDataVirtualRegister, - TR::Register * tempReg1); -#else - /** - * \brief - * Pre-JNI VM helper call builder. - * This is used to build a call to invoke the VM releaseVMAccessMask helper before transitioning from the - * JIT'ed code to native code. - */ - void releaseVMAccessMask(TR::Node * callNode, TR::Register * methodMetaDataVirtualRegister, - TR::Register * methodAddressReg, TR::Register * javaLitOffsetReg, - TR::S390JNICallDataSnippet * jniCallDataSnippet, TR::RegisterDependencyConditions * deps); - /** - * \brief - * Post-JNI VM helper call builder. - * This is used to build a call to invoke the VM acquireVMAccessMask helper after transitioning from the - * native code back to the JIT'ed code. - */ - void acquireVMAccessMask(TR::Node * callNode, TR::Register * javaLitPoolVirtualRegister, - TR::Register * methodMetaDataVirtualRegister); -#endif - }; -} - -#endif /* J9S390JNILINKAGE_INCL */ diff --git a/runtime/compiler/z/codegen/J9S390PrivateLinkage.cpp b/runtime/compiler/z/codegen/J9S390PrivateLinkage.cpp index d2cfdc5f8db..4ba2c536263 100644 --- a/runtime/compiler/z/codegen/J9S390PrivateLinkage.cpp +++ b/runtime/compiler/z/codegen/J9S390PrivateLinkage.cpp @@ -40,7 +40,6 @@ #include "il/TreeTop.hpp" #include "il/TreeTop_inlines.hpp" #include "infra/InterferenceGraph.hpp" -#include "z/codegen/J9S390CHelperLinkage.hpp" #include "z/codegen/OpMemToMem.hpp" #include "z/codegen/S390Evaluator.hpp" #include "z/codegen/S390GenerateInstructions.hpp" @@ -2690,6 +2689,673 @@ TR::S390PrivateLinkage::buildDirectCall(TR::Node * callNode, TR::SymbolReference return gcPoint; } + +void +TR::S390PrivateLinkage::callPreJNICallOffloadCheck(TR::Node * callNode) + { + TR::CodeGenerator * codeGen = cg(); + TR::LabelSymbol * offloadOffRestartLabel = generateLabelSymbol(codeGen); + TR::LabelSymbol * offloadOffSnippetLabel = generateLabelSymbol(codeGen); + TR::SymbolReference * offloadOffSymRef = codeGen->symRefTab()->findOrCreateRuntimeHelper(TR_S390jitPreJNICallOffloadCheck, false, false, false); + + TR::Instruction *gcPoint = generateS390BranchInstruction( + codeGen, TR::InstOpCode::BRC, TR::InstOpCode::COND_BRC, callNode, offloadOffSnippetLabel); + gcPoint->setNeedsGCMap(0); + + codeGen->addSnippet(new (trHeapMemory()) TR::S390HelperCallSnippet(codeGen, callNode, + offloadOffSnippetLabel, offloadOffSymRef, offloadOffRestartLabel)); + generateS390LabelInstruction(codeGen, TR::InstOpCode::LABEL, callNode, offloadOffRestartLabel); + } + +void +TR::S390PrivateLinkage::callPostJNICallOffloadCheck(TR::Node * callNode) + { + TR::CodeGenerator * codeGen = cg(); + TR::LabelSymbol * offloadOnRestartLabel = generateLabelSymbol(codeGen); + TR::LabelSymbol * offloadOnSnippetLabel = generateLabelSymbol(codeGen); + + TR::Instruction *gcPoint = generateS390BranchInstruction( + codeGen, TR::InstOpCode::BRC, TR::InstOpCode::COND_BRC, callNode, offloadOnSnippetLabel); + gcPoint->setNeedsGCMap(0); + TR::SymbolReference * offloadOnSymRef = codeGen->symRefTab()->findOrCreateRuntimeHelper(TR_S390jitPostJNICallOffloadCheck, false, false, false); + codeGen->addSnippet(new (trHeapMemory()) TR::S390HelperCallSnippet(codeGen, callNode, + offloadOnSnippetLabel, offloadOnSymRef, offloadOnRestartLabel)); + generateS390LabelInstruction(codeGen, TR::InstOpCode::LABEL, callNode, offloadOnRestartLabel); + } + +void TR::S390PrivateLinkage::collapseJNIReferenceFrame(TR::Node * callNode, + TR::RealRegister * javaStackPointerRealRegister, + TR::Register * javaLitPoolVirtualRegister, + TR::Register * tempReg) + { + // must check to see if the ref pool was used and clean them up if so--or we + // leave a bunch of pinned garbage behind that screws up the gc quality forever + TR::CodeGenerator * codeGen = cg(); + TR_J9VMBase *fej9 = (TR_J9VMBase *)(fe()); + intptr_t flagValue = fej9->constJNIReferenceFrameAllocatedFlags(); + TR::LabelSymbol * refPoolRestartLabel = generateLabelSymbol(codeGen); + TR::LabelSymbol * refPoolSnippetLabel = generateLabelSymbol(codeGen); + + genLoadAddressConstant(codeGen, callNode, flagValue, tempReg, NULL, NULL, javaLitPoolVirtualRegister); + + generateRXInstruction(codeGen, TR::InstOpCode::getAndOpCode(), callNode, tempReg, + new (trHeapMemory()) TR::MemoryReference(javaStackPointerRealRegister, (int32_t)fej9->constJNICallOutFrameFlagsOffset(), codeGen)); + TR::Instruction *gcPoint = + generateS390BranchInstruction(codeGen, TR::InstOpCode::BRC, TR::InstOpCode::COND_BNE, callNode, refPoolSnippetLabel); + gcPoint->setNeedsGCMap(0); + + TR::SymbolReference * collapseSymRef = cg()->symRefTab()->findOrCreateRuntimeHelper(TR_S390collapseJNIReferenceFrame, false, false, false); + codeGen->addSnippet(new (trHeapMemory()) TR::S390HelperCallSnippet(codeGen, callNode, + refPoolSnippetLabel, collapseSymRef, refPoolRestartLabel)); + generateS390LabelInstruction(cg(), TR::InstOpCode::LABEL, callNode, refPoolRestartLabel); + } + +//JNI Callout frame +// +// |-----| +// | | <-- constJNICallOutFrameSpecialTag() (For jni thunk, constJNICallOutFrameInvisibleTag()) +// 16/32 |-----| +// | | <-- savedPC ( we don't save anything here +// 12/24 |-----| +// | | <-- return address for JNI call +// 8/16 |-----| +// | | <-- constJNICallOutFrameFlags() +// 4/8 ----- +// | | <-- ramMethod for the native method +// ----- <-- stack pointer +// + +// release vm access - use hardware registers because of the control flow +// At this point: arguments for the native routine are all in place already, i.e., if there are +// more than 24 byte worth of arguments, some of them are on the stack. However, +// we potentially go out to call a helper before jumping to the native. +// but the helper call saves and restores all regs +void +TR::S390PrivateLinkage::setupJNICallOutFrame(TR::Node * callNode, + TR::RealRegister * javaStackPointerRealRegister, + TR::Register * methodMetaDataVirtualRegister, + TR::LabelSymbol * returnFromJNICallLabel, + TR::S390JNICallDataSnippet *jniCallDataSnippet) + { + TR::CodeGenerator * codeGen = cg(); + TR_J9VMBase *fej9 = (TR_J9VMBase *)(fe()); + TR::ResolvedMethodSymbol * cs = callNode->getSymbol()->castToResolvedMethodSymbol(); + TR_ResolvedMethod * resolvedMethod = cs->getResolvedMethod(); + TR::Instruction * cursor = NULL; + + int32_t stackAdjust = (-5 * (int32_t)sizeof(intptrj_t)); + + cursor = generateRXYInstruction(codeGen, TR::InstOpCode::LAY, callNode, javaStackPointerRealRegister, generateS390MemoryReference(javaStackPointerRealRegister, stackAdjust, codeGen), cursor); + + setOffsetToLongDispSlot( getOffsetToLongDispSlot() - stackAdjust ); + + + // set up Java Thread + intptrj_t constJNICallOutFrameType = fej9->constJNICallOutFrameType(); + TR_ASSERT( constJNICallOutFrameType < MAX_IMMEDIATE_VAL, "OMR::Z::Linkage::setupJNICallOutFrame constJNICallOutFrameType is too big for MVHI"); + + TR_ASSERT((fej9->thisThreadGetJavaFrameFlagsOffset() == fej9->thisThreadGetJavaLiteralsOffset() + TR::Compiler->om.sizeofReferenceAddress()) && + fej9->thisThreadGetJavaLiteralsOffset() == fej9->thisThreadGetJavaPCOffset() + TR::Compiler->om.sizeofReferenceAddress() + , "The vmthread field order should be pc,literals,jitStackFrameFlags\n"); + + jniCallDataSnippet->setPC(constJNICallOutFrameType); + jniCallDataSnippet->setLiterals(0); + jniCallDataSnippet->setJitStackFrameFlags(0); + + generateSS1Instruction(cg(), TR::InstOpCode::MVC, callNode, 3*(TR::Compiler->om.sizeofReferenceAddress()) - 1, + new (trHeapMemory()) TR::MemoryReference(methodMetaDataVirtualRegister, fej9->thisThreadGetJavaPCOffset(), codeGen), + new (trHeapMemory()) TR::MemoryReference(jniCallDataSnippet->getBaseRegister(), jniCallDataSnippet->getPCOffset(), codeGen)); + + // store out jsp + generateRXInstruction(codeGen, TR::InstOpCode::getStoreOpCode(), callNode, javaStackPointerRealRegister, + new (trHeapMemory()) TR::MemoryReference(methodMetaDataVirtualRegister, + fej9->thisThreadGetJavaSPOffset(), codeGen)); + + // JNI Callout Frame setup + // 0(sp) : RAM method for the native + intptrj_t ramMethod = (uintptrj_t) resolvedMethod->resolvedMethodAddress(); + jniCallDataSnippet->setRAMMethod(ramMethod); + + // 4[8](sp) : flags + intptrj_t flags = fej9->constJNICallOutFrameFlags(); + jniCallDataSnippet->setJNICallOutFrameFlags(flags); + + // 8[16](sp) : return address (savedCP) + jniCallDataSnippet->setReturnFromJNICall(returnFromJNICallLabel); + + // 12[24](sp) : savedPC + jniCallDataSnippet->setSavedPC(0); + + // 16[32](sp) : tag bits (savedA0) + intptr_t tagBits = fej9->constJNICallOutFrameSpecialTag(); + // if the current method is simply a wrapper for the JNI call, hide the call-out stack frame + if (resolvedMethod == comp()->getCurrentMethod()) + { + tagBits |= fej9->constJNICallOutFrameInvisibleTag(); + } + + jniCallDataSnippet->setTagBits(tagBits); + + generateSS1Instruction(cg(), TR::InstOpCode::MVC, callNode, -stackAdjust - 1, + new (trHeapMemory()) TR::MemoryReference(javaStackPointerRealRegister, 0, codeGen), + new (trHeapMemory()) TR::MemoryReference(jniCallDataSnippet->getBaseRegister(), jniCallDataSnippet->getJNICallOutFrameDataOffset(), codeGen)); + + } + + +/** + * release vm access - use hardware registers because of the control flow + * At this point: arguments for the native routine are all in place already, i.e., if there are + * more than 24 byte worth of arguments, some of them are on the stack. However, + * we potentially go out to call a helper before jumping to the native. + * but the helper call saves and restores all regs + */ +void TR::J9S390JNILinkage::releaseVMAccessMask(TR::Node * callNode, + TR::Register * methodMetaDataVirtualRegister, TR::Register * methodAddressReg, TR::Register * javaLitOffsetReg, + TR::S390JNICallDataSnippet * jniCallDataSnippet, TR::RegisterDependencyConditions * deps) + { + TR::LabelSymbol * loopHead = generateLabelSymbol(self()->cg()); + TR::LabelSymbol * longReleaseLabel = generateLabelSymbol(self()->cg()); + TR::LabelSymbol * longReleaseSnippetLabel = generateLabelSymbol(self()->cg()); + TR::LabelSymbol * doneLabel = generateLabelSymbol(self()->cg()); + TR_J9VMBase *fej9 = (TR_J9VMBase *)(self()->fe()); + + intptrj_t aValue = fej9->constReleaseVMAccessMask(); //0xfffffffffffdffdf + jniCallDataSnippet->setConstReleaseVMAccessMask(aValue); + + generateRXInstruction(self()->cg(), TR::InstOpCode::getLoadOpCode(), callNode, methodAddressReg, + generateS390MemoryReference(methodMetaDataVirtualRegister, + fej9->thisThreadGetPublicFlagsOffset(), self()->cg())); + + + TR::Instruction * label = generateS390LabelInstruction(self()->cg(), TR::InstOpCode::LABEL, callNode, loopHead); + label->setStartInternalControlFlow(); + + + aValue = fej9->constReleaseVMAccessOutOfLineMask(); //0x340001 + jniCallDataSnippet->setConstReleaseVMAccessOutOfLineMask(aValue); + + generateRRInstruction(self()->cg(), TR::InstOpCode::getLoadRegOpCode(), callNode, javaLitOffsetReg, methodAddressReg); + generateRXInstruction(self()->cg(), TR::InstOpCode::getAndOpCode(), callNode, javaLitOffsetReg, + generateS390MemoryReference(jniCallDataSnippet->getBaseRegister(), jniCallDataSnippet->getConstReleaseVMAccessOutOfLineMaskOffset(), self()->cg())); + + TR::Instruction * gcPoint = (TR::Instruction *) generateS390BranchInstruction( + self()->cg(), TR::InstOpCode::BRC, TR::InstOpCode::COND_BNE, callNode, longReleaseSnippetLabel); + gcPoint->setNeedsGCMap(0); + + generateRRInstruction(self()->cg(), TR::InstOpCode::getLoadRegOpCode(), callNode, javaLitOffsetReg, methodAddressReg); + generateRXInstruction(self()->cg(), TR::InstOpCode::getAndOpCode(), callNode, javaLitOffsetReg, + generateS390MemoryReference(jniCallDataSnippet->getBaseRegister(), jniCallDataSnippet->getConstReleaseVMAccessMaskOffset(), self()->cg())); + generateRSInstruction(self()->cg(), TR::InstOpCode::getCmpAndSwapOpCode(), callNode, methodAddressReg, javaLitOffsetReg, + generateS390MemoryReference(methodMetaDataVirtualRegister, + fej9->thisThreadGetPublicFlagsOffset(), self()->cg())); + + + //get existing post conditions on the registers parameters and create a new post cond for the internal control flow + TR::RegisterDependencyConditions * postDeps = new (self()->trHeapMemory()) TR::RegisterDependencyConditions(0, 3, self()->cg()); + TR::RealRegister::RegNum realReg; + int32_t regPos = deps->searchPostConditionRegisterPos(methodMetaDataVirtualRegister); + if (regPos >= 0) + { + realReg = deps->getPostConditions()->getRegisterDependency(regPos)->getRealRegister(); + postDeps->addPostCondition(methodMetaDataVirtualRegister, realReg); + } + else + postDeps->addPostCondition(methodMetaDataVirtualRegister, TR::RealRegister::AssignAny); + + regPos = deps->searchPostConditionRegisterPos(methodAddressReg); + if (regPos >= 0) + { + realReg = deps->getPostConditions()->getRegisterDependency(regPos)->getRealRegister(); + postDeps->addPostCondition(methodAddressReg, realReg); + } + else + postDeps->addPostCondition(methodAddressReg, TR::RealRegister::AssignAny); + + regPos = deps->searchPostConditionRegisterPos(javaLitOffsetReg); + if (regPos >= 0) + { + realReg = deps->getPostConditions()->getRegisterDependency(regPos)->getRealRegister(); + postDeps->addPostCondition(javaLitOffsetReg, realReg); + } + else + postDeps->addPostCondition(javaLitOffsetReg, TR::RealRegister::AssignAny); + + + TR::Instruction * br = generateS390BranchInstruction(self()->cg(), TR::InstOpCode::BRC, TR::InstOpCode::COND_BNE, callNode, loopHead); + br->setEndInternalControlFlow(); + br->setDependencyConditions(postDeps); + + generateS390LabelInstruction(self()->cg(), TR::InstOpCode::LABEL, callNode, doneLabel); + + + self()->cg()->addSnippet(new (self()->trHeapMemory()) TR::S390HelperCallSnippet(self()->cg(), callNode, longReleaseSnippetLabel, + self()->comp()->getSymRefTab()->findOrCreateReleaseVMAccessSymbolRef(self()->comp()->getJittedMethodSymbol()), doneLabel)); + // end of release vm access (spin lock) + } + + +void TR::J9S390JNILinkage::acquireVMAccessMask(TR::Node * callNode, TR::Register * javaLitPoolVirtualRegister, + TR::Register * methodMetaDataVirtualRegister, TR::Register * methodAddressReg, TR::Register * javaLitOffsetReg) + { + // start of acquire vm access + + // WARNING: + // As java stack is not yet restored , Make sure that no instruction in this function + // should use stack. + // If instruction uses literal pool, it must only be to do load, and such instruction's memory reference should be marked MemRefMustNotSpill + // so that in case of long disp, we will resue the target reg as a scratch reg + + TR_J9VMBase *fej9 = (TR_J9VMBase *)(self()->fe()); + intptrj_t aValue = fej9->constAcquireVMAccessOutOfLineMask(); + + TR::Instruction * loadInstr = (TR::Instruction *) genLoadAddressConstant(self()->cg(), callNode, aValue, methodAddressReg, NULL, NULL, javaLitPoolVirtualRegister); + switch (loadInstr->getKind()) + { + case TR::Instruction::IsRX: + case TR::Instruction::IsRXE: + case TR::Instruction::IsRXY: + case TR::Instruction::IsRXYb: + ((TR::S390RXInstruction *)loadInstr)->getMemoryReference()->setMemRefMustNotSpill(); + break; + default: + break; + } + + generateRRInstruction(self()->cg(), TR::InstOpCode::getXORRegOpCode(), callNode, javaLitOffsetReg, javaLitOffsetReg); + + TR::LabelSymbol * longAcquireLabel = generateLabelSymbol(self()->cg()); + TR::LabelSymbol * longAcquireSnippetLabel = generateLabelSymbol(self()->cg()); + TR::LabelSymbol * acquireDoneLabel = generateLabelSymbol(self()->cg()); + generateRSInstruction(cg(), TR::InstOpCode::getCmpAndSwapOpCode(), callNode, javaLitOffsetReg, methodAddressReg, + generateS390MemoryReference(methodMetaDataVirtualRegister, + (int32_t)fej9->thisThreadGetPublicFlagsOffset(), self()->cg())); + TR::Instruction *gcPoint = (TR::Instruction *) generateS390BranchInstruction(self()->cg(), TR::InstOpCode::BRC, TR::InstOpCode::COND_BNE, callNode, longAcquireSnippetLabel); + gcPoint->setNeedsGCMap(0); + + self()->cg()->addSnippet(new (self()->trHeapMemory()) TR::S390HelperCallSnippet(self()->cg(), callNode, longAcquireSnippetLabel, + self()->comp()->getSymRefTab()->findOrCreateAcquireVMAccessSymbolRef(self()->comp()->getJittedMethodSymbol()), acquireDoneLabel)); + generateS390LabelInstruction(self()->cg(), TR::InstOpCode::LABEL, callNode, acquireDoneLabel); + // end of acquire vm accessa + } + +#ifdef J9VM_INTERP_ATOMIC_FREE_JNI + +/** + * \brief + * Build the atomic-free release VM access sequence for JNI dispatch. + * + * \details + * This is the atomic-free JNI design and works in conjunction with VMAccess.cpp atomic-free JNI changes. + * + * In the JNI dispatch sequence, a release-vm-access action is performed before the branch to native code; and an acquire-vm-access + * is done after the thread execution returns from the native call. Both of the actions require synchronization between the + * application thread and the GC thread. This was previously implemented with the atomic compare-and-swap (CS) instruction, which is slow in nature. + * + * To speed up the JNI acquire and release access actions (the fast path), a store-load sequence is generated by this evaluator + * to replace the CS instruction. Normally, the fast path ST-LD are not serialized and can be done out-of-order for higher performance. Synchronization + * burden is offloaded to the slow path. + * + * The slow path is where a thread tries to acquire exclusive vm access. The slow path should be taken proportionally less often than the fast + * path. Should the slow path be taken, that thread will be penalized by calling a slow flushProcessWriteBuffer() routine so that all threads + * can momentarily synchronize memory writes. Having fast and slow paths makes the atomic-free JNI design asymmetric. + * + * Note that the z/OS currently does not support the asymmetric algorithm. Hence, a serialization instruction is required between the + * store and the load. + * +*/ +void +TR::J9S390JNILinkage::releaseVMAccessMaskAtomicFree(TR::Node * callNode, + TR::Register * methodMetaDataVirtualRegister, + TR::Register * tempReg1) + { + TR_J9VMBase *fej9 = (TR_J9VMBase *)fe(); + TR::CodeGenerator* cg = self()->cg(); + TR::Compilation* comp = self()->comp(); + + // Store a 1 into vmthread->inNative + generateSILInstruction(cg, TR::InstOpCode::getMoveHalfWordImmOpCode(), callNode, + generateS390MemoryReference(methodMetaDataVirtualRegister, offsetof(J9VMThread, inNative), cg), + 1); + +#if !defined(J9VM_INTERP_ATOMIC_FREE_JNI_USES_FLUSH) + generateSerializationInstruction(cg, callNode, NULL); +#endif + + // Compare vmthread public flag with J9_PUBLIC_FLAGS_VM_ACCESS + generateRXInstruction(cg, TR::InstOpCode::getLoadOpCode(), callNode, tempReg1, + generateS390MemoryReference(methodMetaDataVirtualRegister, fej9->thisThreadGetPublicFlagsOffset(), cg)); + + TR::LabelSymbol * longReleaseSnippetLabel = generateLabelSymbol(cg); + TR::LabelSymbol * longReleaseRestartLabel = generateLabelSymbol(cg); + + TR_ASSERT_FATAL(J9_PUBLIC_FLAGS_VM_ACCESS >= MIN_IMMEDIATE_BYTE_VAL && J9_PUBLIC_FLAGS_VM_ACCESS <= MAX_IMMEDIATE_BYTE_VAL, "VM access bit must be immediate"); + generateRIEInstruction(cg, TR::InstOpCode::getCmpImmBranchRelOpCode(), callNode, tempReg1, J9_PUBLIC_FLAGS_VM_ACCESS, longReleaseSnippetLabel, TR::InstOpCode::COND_BNE); + cg->addSnippet(new (self()->trHeapMemory()) TR::S390HelperCallSnippet(cg, + callNode, longReleaseSnippetLabel, + comp->getSymRefTab()->findOrCreateAcquireVMAccessSymbolRef(comp->getJittedMethodSymbol()), + longReleaseRestartLabel)); + + generateS390LabelInstruction(cg, TR::InstOpCode::LABEL, callNode, longReleaseRestartLabel); + } + +/** + * \brief + * Build the atomic-free acquire VM access sequence for JNI dispatch. + * + * */ +void +TR::J9S390JNILinkage::acquireVMAccessMaskAtomicFree(TR::Node * callNode, + TR::Register * methodMetaDataVirtualRegister, + TR::Register * tempReg1) + { + TR_J9VMBase *fej9 = (TR_J9VMBase *)fe(); + TR::CodeGenerator* cg = self()->cg(); + TR::Compilation* comp = self()->comp(); + + // Zero vmthread->inNative, which is a UDATA field + generateSS1Instruction(cg, TR::InstOpCode::XC, callNode, TR::Compiler->om.sizeofReferenceAddress() - 1, + generateS390MemoryReference(methodMetaDataVirtualRegister, offsetof(J9VMThread, inNative), cg), + generateS390MemoryReference(methodMetaDataVirtualRegister, offsetof(J9VMThread, inNative), cg)); + +#if !defined(J9VM_INTERP_ATOMIC_FREE_JNI_USES_FLUSH) + generateSerializationInstruction(cg, callNode, NULL); +#endif + + // Compare vmthread public flag with J9_PUBLIC_FLAGS_VM_ACCESS + generateRXInstruction(cg, TR::InstOpCode::getLoadOpCode(), callNode, tempReg1, + generateS390MemoryReference(methodMetaDataVirtualRegister, fej9->thisThreadGetPublicFlagsOffset(), cg)); + + TR::LabelSymbol * longAcquireSnippetLabel = generateLabelSymbol(cg); + TR::LabelSymbol * longAcquireRestartLabel = generateLabelSymbol(cg); + + TR_ASSERT_FATAL(J9_PUBLIC_FLAGS_VM_ACCESS >= MIN_IMMEDIATE_BYTE_VAL && J9_PUBLIC_FLAGS_VM_ACCESS <= MAX_IMMEDIATE_BYTE_VAL, "VM access bit must be immediate"); + generateRIEInstruction(cg, TR::InstOpCode::getCmpImmBranchRelOpCode(), callNode, tempReg1, J9_PUBLIC_FLAGS_VM_ACCESS, longAcquireSnippetLabel, TR::InstOpCode::COND_BNE); + + cg->addSnippet(new (self()->trHeapMemory()) TR::S390HelperCallSnippet(cg, + callNode, longAcquireSnippetLabel, + comp->getSymRefTab()->findOrCreateAcquireVMAccessSymbolRef(comp->getJittedMethodSymbol()), + longAcquireRestartLabel)); + + generateS390LabelInstruction(cg, TR::InstOpCode::LABEL, callNode, longAcquireRestartLabel); + } +#endif + +void TR::J9S390JNILinkage::checkException(TR::Node * callNode, + TR::Register * methodMetaDataVirtualRegister, + TR::Register * tempReg) + { + TR_J9VMBase *fej9 = (TR_J9VMBase *)(fe()); + // check exception + TR::LabelSymbol * exceptionRestartLabel = generateLabelSymbol(self()->cg()); + TR::LabelSymbol * exceptionSnippetLabel = generateLabelSymbol(self()->cg()); + generateRXInstruction(self()->cg(), TR::InstOpCode::getLoadOpCode(), callNode, tempReg, + new (self()->trHeapMemory()) TR::MemoryReference(methodMetaDataVirtualRegister, fej9->thisThreadGetCurrentExceptionOffset(), self()->cg())); + + TR::Instruction *gcPoint = (TR::Instruction *) generateS390CompareAndBranchInstruction(self()->cg(), + TR::InstOpCode::getCmpOpCode(), callNode, tempReg, (signed char)0, TR::InstOpCode::COND_BNE, exceptionSnippetLabel, false, true); + gcPoint->setNeedsGCMap(0); + + self()->cg()->addSnippet(new (self()->trHeapMemory()) TR::S390HelperCallSnippet(self()->cg(), callNode, exceptionSnippetLabel, + self()->comp()->getSymRefTab()->findOrCreateThrowCurrentExceptionSymbolRef(self()->comp()->getJittedMethodSymbol()), exceptionRestartLabel)); + generateS390LabelInstruction(self()->cg(), TR::InstOpCode::LABEL, callNode, exceptionRestartLabel); + } + + +TR::Register * TR::J9S390JNILinkage::buildDirectDispatch(TR::Node * callNode) + { + if (comp()->getOption(TR_TraceCG)) + traceMsg(comp(), "\nbuildDirectDispatch\n"); + + TR::CodeGenerator * codeGen = cg(); + TR_J9VMBase *fej9 = (TR_J9VMBase *)(fe()); + TR::SystemLinkage * systemLinkage = (TR::SystemLinkage *) cg()->getLinkage(TR_System); + TR::LabelSymbol * returnFromJNICallLabel = generateLabelSymbol(cg()); + TR::RegisterDependencyConditions * deps; + int32_t numDeps = systemLinkage->getNumberOfDependencyGPRegisters(); + if (cg()->supportsHighWordFacility() && !comp()->getOption(TR_DisableHighWordRA)) + numDeps += 16; //HPRs need to be spilled + if (cg()->getSupportsVectorRegisters()) + numDeps += 32; //VRFs need to be spilled + + // 70896 Remove DEPEND instruction and merge glRegDeps to call deps + // *Speculatively* increase numDeps for dependencies from glRegDeps + // which is added right before callNativeFunction. + // GlobalRegDeps should not add any more children after here. + TR::RegisterDependencyConditions *glRegDeps; + TR::Node *GlobalRegDeps; + + bool hasGlRegDeps = (callNode->getNumChildren() >= 1) && + (callNode->getChild(callNode->getNumChildren()-1)->getOpCodeValue() == TR::GlRegDeps); + if(hasGlRegDeps) + { + GlobalRegDeps = callNode->getChild(callNode->getNumChildren()-1); + numDeps += GlobalRegDeps->getNumChildren(); + } + + deps = generateRegisterDependencyConditions(numDeps, numDeps, cg()); + int64_t killMask = -1; + TR::Register *vftReg = NULL; + TR::S390JNICallDataSnippet * jniCallDataSnippet = NULL; + TR::RealRegister * javaStackPointerRealRegister = getStackPointerRealRegister(); + TR::RealRegister * methodMetaDataRealRegister = getMethodMetaDataRealRegister(); + TR::RealRegister * javaLitPoolRealRegister = getLitPoolRealRegister(); + + TR::Register * javaLitPoolVirtualRegister = javaLitPoolRealRegister; + TR::Register * methodMetaDataVirtualRegister = methodMetaDataRealRegister; + + TR::Register * methodAddressReg = NULL; + TR::Register * javaLitOffsetReg = NULL; + intptrj_t targetAddress = (intptrj_t) 0; + TR::DataType returnType = TR::NoType; + int8_t numTempRegs = -1; + comp()->setHasNativeCall(); + + if (codeGen->getSupportsRuntimeInstrumentation()) + TR::TreeEvaluator::generateRuntimeInstrumentationOnOffSequence(codeGen, TR::InstOpCode::RIOFF, callNode); + + TR::ResolvedMethodSymbol * cs = callNode->getSymbol()->castToResolvedMethodSymbol(); + TR_ResolvedMethod * resolvedMethod = cs->getResolvedMethod(); + bool isFastJNI = true; + bool isPassJNIThread = !fej9->jniDoNotPassThread(resolvedMethod); + bool isPassReceiver = !fej9->jniDoNotPassReceiver(resolvedMethod); + bool isJNIGCPoint = !fej9->jniNoGCPoint(resolvedMethod); + bool isJNICallOutFrame = !fej9->jniNoNativeMethodFrame(resolvedMethod); + bool isReleaseVMAccess = !fej9->jniRetainVMAccess(resolvedMethod); + bool isJavaOffLoadCheck = false; + bool isAcquireVMAccess = isReleaseVMAccess; + bool isCollapseJNIReferenceFrame = !fej9->jniNoSpecialTeardown(resolvedMethod); + bool isCheckException = !fej9->jniNoExceptionsThrown(resolvedMethod); + bool isUnwrapAddressReturnValue = !fej9->jniDoNotWrapObjects(resolvedMethod); + bool isKillAllUnlockedGPRs = isJNIGCPoint; + + killMask = killAndAssignRegister(killMask, deps, &methodAddressReg, (TR::Compiler->target.isLinux()) ? TR::RealRegister::GPR1 : TR::RealRegister::GPR9 , codeGen, true); + killMask = killAndAssignRegister(killMask, deps, &javaLitOffsetReg, TR::RealRegister::GPR11, codeGen, true); + + targetAddress = (intptrj_t) resolvedMethod->startAddressForJNIMethod(comp()); + returnType = resolvedMethod->returnType(); + + static char * disablePureFn = feGetEnv("TR_DISABLE_PURE_FUNC_RECOGNITION"); + if (cs->canDirectNativeCall()) + { + isReleaseVMAccess = false; + isAcquireVMAccess = false; + isKillAllUnlockedGPRs = false; + isJNIGCPoint = false; + isCheckException = false; + isJNICallOutFrame = false; + } + if (cs->isPureFunction() && (disablePureFn == NULL)) + { + isReleaseVMAccess=false; + isAcquireVMAccess=false; + isCheckException = false; + } + if ((fej9->isJavaOffloadEnabled() && static_cast(resolvedMethod)->methodIsNotzAAPEligible()) || (fej9->CEEHDLREnabled() && isJNICallOutFrame)) + isJavaOffLoadCheck = true; + + + if (comp()->getOption(TR_TraceCG)) + traceMsg(comp(), "isPassReceiver: %d, isPassJNIThread: %d, isJNIGCPoint: %d, isJNICallOutFrame:%d, isReleaseVMAccess: %d, isCollapseJNIReferenceFrame: %d, isJNIGCPoint: %d\n", isPassReceiver, isPassJNIThread, isJNIGCPoint, isJNICallOutFrame, isReleaseVMAccess, isCollapseJNIReferenceFrame, isJNIGCPoint); + + if (isPassJNIThread) + { + //First param for JNI call in JNIEnv pointer + TR::Register * jniEnvRegister = cg()->allocateRegister(); + deps->addPreCondition(jniEnvRegister, systemLinkage->getIntegerArgumentRegister(0)); + generateRRInstruction(codeGen, TR::InstOpCode::getLoadRegOpCode(), callNode, + jniEnvRegister, methodMetaDataVirtualRegister); + } + + // JNI dispatch does not allow for any object references to survive in preserved registers + // they are saved onto the system stack, which the stack walker has no way of accessing. + // Hence, ensure we kill all preserved HPRs (6-12) as well + if (cg()->supportsHighWordFacility() && !comp()->getOption(TR_DisableHighWordRA)) + { + TR::Register *dummyReg = NULL; + killAndAssignRegister(killMask, deps, &dummyReg, REGNUM(TR::RealRegister::HPR6), codeGen, true, true ); + killAndAssignRegister(killMask, deps, &dummyReg, REGNUM(TR::RealRegister::HPR7), codeGen, true, true ); + killAndAssignRegister(killMask, deps, &dummyReg, REGNUM(TR::RealRegister::HPR8), codeGen, true, true ); + killAndAssignRegister(killMask, deps, &dummyReg, REGNUM(TR::RealRegister::HPR9), codeGen, true, true ); + killAndAssignRegister(killMask, deps, &dummyReg, REGNUM(TR::RealRegister::HPR10), codeGen, true, true ); + killAndAssignRegister(killMask, deps, &dummyReg, REGNUM(TR::RealRegister::HPR11), codeGen, true, true ); + killAndAssignRegister(killMask, deps, &dummyReg, REGNUM(TR::RealRegister::HPR12), codeGen, true, true ); + } + + setupRegisterDepForLinkage(callNode, TR_JNIDispatch, deps, killMask, systemLinkage, GlobalRegDeps, hasGlRegDeps, &methodAddressReg, javaLitOffsetReg); + + setupBuildArgForLinkage(callNode, TR_JNIDispatch, deps, isFastJNI, isPassReceiver, killMask, GlobalRegDeps, hasGlRegDeps, systemLinkage); + + if (isJNICallOutFrame || isReleaseVMAccess) + { + TR::Register * JNISnippetBaseReg = NULL; + killMask = killAndAssignRegister(killMask, deps, &JNISnippetBaseReg, TR::RealRegister::GPR12, codeGen, true); + jniCallDataSnippet = new (trHeapMemory()) TR::S390JNICallDataSnippet(cg(), callNode); + cg()->addSnippet(jniCallDataSnippet); + jniCallDataSnippet->setBaseRegister(JNISnippetBaseReg); + new (trHeapMemory()) TR::S390RILInstruction(TR::InstOpCode::LARL, callNode, + jniCallDataSnippet->getBaseRegister(), jniCallDataSnippet, codeGen); + jniCallDataSnippet->setTargetAddress(targetAddress); + } + + if (isJNICallOutFrame) + { + // Sets up PC, Stack pointer and literals offset slots. + setupJNICallOutFrame(callNode, javaStackPointerRealRegister, methodMetaDataVirtualRegister, + returnFromJNICallLabel, jniCallDataSnippet); + } + else + { + // store java stack pointer + generateRXInstruction(codeGen, TR::InstOpCode::getStoreOpCode(), callNode, javaStackPointerRealRegister, + new (trHeapMemory()) TR::MemoryReference(methodMetaDataVirtualRegister, (int32_t)fej9->thisThreadGetJavaSPOffset(), codeGen)); + + + auto* literalOffsetMemoryReference = new (trHeapMemory()) TR::MemoryReference(methodMetaDataVirtualRegister, (int32_t)fej9->thisThreadGetJavaLiteralsOffset(), codeGen); + + // Set up literal offset slot to zero + if (codeGen->getS390ProcessorInfo()->supportsArch(TR_S390ProcessorInfo::TR_z10)) + { + generateSILInstruction(codeGen, TR::InstOpCode::getMoveHalfWordImmOpCode(), callNode, literalOffsetMemoryReference, 0); + } + else + { + generateRIInstruction(codeGen, TR::InstOpCode::getLoadHalfWordImmOpCode(), callNode, javaLitOffsetReg, 0); + + generateRXInstruction(codeGen, TR::InstOpCode::getStoreOpCode(), callNode, javaLitOffsetReg, literalOffsetMemoryReference); + } + } + + if (isReleaseVMAccess) + { +#ifdef J9VM_INTERP_ATOMIC_FREE_JNI + releaseVMAccessMaskAtomicFree(callNode, methodMetaDataVirtualRegister, methodAddressReg); +#else + releaseVMAccessMask(callNode, methodMetaDataVirtualRegister, methodAddressReg, javaLitOffsetReg, jniCallDataSnippet, deps); +#endif + } + + //Turn off Java Offload if calling user native + if (isJavaOffLoadCheck) + { + callPreJNICallOffloadCheck(callNode); + } + + // Generate a call to the native function + TR::Register * javaReturnRegister = systemLinkage->callNativeFunction( + callNode, deps, targetAddress, methodAddressReg, javaLitOffsetReg, returnFromJNICallLabel, + jniCallDataSnippet, isJNIGCPoint); + + // restore java stack pointer + generateRXInstruction(codeGen, TR::InstOpCode::getLoadOpCode(), callNode, javaStackPointerRealRegister, + new (trHeapMemory()) TR::MemoryReference(methodMetaDataVirtualRegister, (int32_t)fej9->thisThreadGetJavaSPOffset(), codeGen)); + + //Turn on Java Offload + if (isJavaOffLoadCheck) + { + callPostJNICallOffloadCheck(callNode); + } + + if (isAcquireVMAccess) + { +#ifdef J9VM_INTERP_ATOMIC_FREE_JNI + acquireVMAccessMaskAtomicFree(callNode, methodMetaDataVirtualRegister, methodAddressReg); +#else + acquireVMAccessMask(callNode, javaLitPoolVirtualRegister, methodMetaDataVirtualRegister, methodAddressReg, javaLitOffsetReg); +#endif + } + + + generateRXInstruction(codeGen, TR::InstOpCode::getAddOpCode(), callNode, javaStackPointerRealRegister, + new (trHeapMemory()) TR::MemoryReference(methodMetaDataVirtualRegister, (int32_t)fej9->thisThreadGetJavaLiteralsOffset(), codeGen)); + + isUnwrapAddressReturnValue = (isUnwrapAddressReturnValue && + (returnType == TR::Address) && + (javaReturnRegister!= NULL) && + (javaReturnRegister->getKind() == TR_GPR)); + + if (isUnwrapAddressReturnValue) + { + TR::LabelSymbol * tempLabel = generateLabelSymbol(codeGen); + TR::Instruction* start = generateS390CompareAndBranchInstruction(codeGen, TR::InstOpCode::getCmpOpCode(), callNode, javaReturnRegister, (signed char)0, TR::InstOpCode::COND_BE, tempLabel); + start->setStartInternalControlFlow(); + generateRXInstruction(codeGen, TR::InstOpCode::getLoadOpCode(), callNode, javaReturnRegister, + generateS390MemoryReference(javaReturnRegister, 0, codeGen)); + + + int32_t regPos = deps->searchPostConditionRegisterPos(javaReturnRegister); + TR::RealRegister::RegNum realReg = deps->getPostConditions()->getRegisterDependency(regPos)->getRealRegister(); + TR::RegisterDependencyConditions * postDeps = new (trHeapMemory()) TR::RegisterDependencyConditions(0, 1, cg()); + postDeps->addPostCondition(javaReturnRegister, realReg); + + TR::Instruction* end =generateS390LabelInstruction(codeGen, TR::InstOpCode::LABEL, callNode, tempLabel); + end->setEndInternalControlFlow(); + end->setDependencyConditions(postDeps); + } + + if (isCollapseJNIReferenceFrame) + { + collapseJNIReferenceFrame(callNode, javaStackPointerRealRegister, javaLitPoolVirtualRegister, methodAddressReg); + } + + // Restore the JIT frame + if (isJNICallOutFrame) + { + generateRXInstruction(codeGen, TR::InstOpCode::LA, callNode, javaStackPointerRealRegister, + generateS390MemoryReference(javaStackPointerRealRegister, 5 * sizeof(intptrj_t), codeGen)); + + setOffsetToLongDispSlot(getOffsetToLongDispSlot() - (5 * (int32_t)sizeof(intptrj_t)) ); + } + + if (isCheckException) + { + checkException(callNode, methodMetaDataVirtualRegister, methodAddressReg); + } + + OMR::Z::Linkage::generateDispatchReturnLable(callNode, codeGen, deps, javaReturnRegister, hasGlRegDeps, GlobalRegDeps); + return javaReturnRegister; + } + //////////////////////////////////////////////////////////////////////////////// // TR::S390PrivateLinkage::doNotKillSpecialRegsForBuildArgs - Do not kill // special regs (java stack ptr, system stack ptr, and method metadata reg) @@ -3066,6 +3732,12 @@ TR::S390PrivateLinkage::setupBuildArgForLinkage(TR::Node * callNode, TR_Dispatch OMR::Z::Linkage::setupBuildArgForLinkage(callNode, dispatchType, deps, isFastJNI, isPassReceiver, killMask, GlobalRegDeps, hasGlRegDeps, systemLinkage); + // omr todo: this should be cleaned up once the logic of other linkage related method is cleaned up + // basically JNIDispatch will perform the stuff after this statement and hence returning here + // to avoid executing stuff twice...should be fixed in conjunction with JNIDispatch + if (dispatchType == TR_JNIDispatch) return; + + TR::S390PrivateLinkage * privateLinkage = (TR::S390PrivateLinkage *) cg()->getLinkage(TR_Private); TR::RealRegister * javaStackPointerRealRegister = privateLinkage->getStackPointerRealRegister(); TR::Register * methodMetaDataVirtualRegister = privateLinkage->getMethodMetaDataRealRegister(); @@ -3146,3 +3818,9 @@ TR::S390PrivateLinkage::setupRegisterDepForLinkage(TR::Node * callNode, TR_Dispa } +TR::J9S390JNILinkage::J9S390JNILinkage(TR::CodeGenerator * cg, TR_S390LinkageConventions elc, TR_LinkageConventions lc) + :TR::S390PrivateLinkage(cg, elc, lc) + { + } + + diff --git a/runtime/compiler/z/codegen/J9S390PrivateLinkage.hpp b/runtime/compiler/z/codegen/J9S390PrivateLinkage.hpp index 9a174ba4a34..e54d7ebeafd 100644 --- a/runtime/compiler/z/codegen/J9S390PrivateLinkage.hpp +++ b/runtime/compiler/z/codegen/J9S390PrivateLinkage.hpp @@ -121,6 +121,18 @@ class S390PrivateLinkage : public TR::Linkage TR::RegisterDependencyConditions * dependencies, int32_t argSize); virtual void mapIncomingParms(TR::ResolvedMethodSymbol *method); + + void callPreJNICallOffloadCheck(TR::Node * callNode); + void callPostJNICallOffloadCheck(TR::Node * callNode); + void collapseJNIReferenceFrame(TR::Node * callNode, TR::RealRegister * javaStackPointerRealRegister, + TR::Register * javaLitPoolVirtualRegister, TR::Register * tempReg); + + void setupJNICallOutFrame(TR::Node * callNode, + TR::RealRegister * javaStackPointerRealRegister, + TR::Register * methodMetaDataVirtualRegister, + TR::LabelSymbol * returnFromJNICallLabel, + TR::S390JNICallDataSnippet *jniCallDataSnippet); + }; @@ -138,6 +150,31 @@ class S390HelperLinkage : public TR::S390PrivateLinkage setProperty(ParmsInReverseOrder); } }; + +class J9S390JNILinkage : public TR::S390PrivateLinkage + { +public: + + J9S390JNILinkage(TR::CodeGenerator * cg, TR_S390LinkageConventions elc=TR_JavaPrivate, TR_LinkageConventions lc=TR_J9JNILinkage); + virtual TR::Register * buildDirectDispatch(TR::Node * callNode); + + void checkException(TR::Node * callNode, TR::Register *methodMetaDataVirtualRegister, TR::Register * tempReg); + void releaseVMAccessMask(TR::Node * callNode, TR::Register * methodMetaDataVirtualRegister, + TR::Register * methodAddressReg, TR::Register * javaLitOffsetReg, TR::S390JNICallDataSnippet * jniCallDataSnippet, TR::RegisterDependencyConditions * deps); + void acquireVMAccessMask(TR::Node * callNode, TR::Register * javaLitPoolVirtualRegister, + TR::Register * methodMetaDataVirtualRegister, TR::Register * methodAddressReg, TR::Register * javaLitOffsetReg); + +#ifdef J9VM_INTERP_ATOMIC_FREE_JNI + void releaseVMAccessMaskAtomicFree(TR::Node * callNode, + TR::Register * methodMetaDataVirtualRegister, + TR::Register * tempReg1); + + void acquireVMAccessMaskAtomicFree(TR::Node * callNode, + TR::Register * methodMetaDataVirtualRegister, + TR::Register * tempReg1); +#endif /* J9VM_INTERP_ATOMIC_FREE_JNI */ + }; + } #endif /* J9S390PRIVATELINKAGE_INCL */ diff --git a/runtime/compiler/z/codegen/J9S390SystemLinkage.cpp b/runtime/compiler/z/codegen/J9S390SystemLinkage.cpp index c2db3677f0d..3e1fafa9d82 100644 --- a/runtime/compiler/z/codegen/J9S390SystemLinkage.cpp +++ b/runtime/compiler/z/codegen/J9S390SystemLinkage.cpp @@ -399,10 +399,7 @@ TR::J9S390zOSSystemLinkage::generateInstructionsForCall(TR::Node * callNode, TR: * Hence, splitting the dependencies to avoid spill instructions. */ TR::RegisterDependencyConditions* callPreDeps = new (self()->trHeapMemory()) TR::RegisterDependencyConditions(deps->getPreConditions(), NULL, deps->getAddCursorForPre(), 0, codeGen); - TR::RegisterDependencyConditions* callPostDeps = NULL; - - if (jniCallDataSnippet == NULL) - callPostDeps = new (self()->trHeapMemory()) TR::RegisterDependencyConditions(NULL, deps->getPostConditions(), 0, deps->getAddCursorForPost(), codeGen); + TR::RegisterDependencyConditions* callPostDeps = new (self()->trHeapMemory()) TR::RegisterDependencyConditions(NULL, deps->getPostConditions(), 0, deps->getAddCursorForPost(), codeGen); gcPoint = generateRRInstruction(codeGen, TR::InstOpCode::BASR, callNode, systemReturnAddressRegister, systemEntryPointRegister, callPreDeps); if (isJNIGCPoint) @@ -411,11 +408,7 @@ TR::J9S390zOSSystemLinkage::generateInstructionsForCall(TR::Node * callNode, TR: TR::Instruction * cursor = generateS390LabelInstruction(codeGen, TR::InstOpCode::LABEL, callNode, returnFromJNICallLabel); cursor = genCallNOPAndDescriptor(cursor, callNode, callNode, TR_XPLinkCallType_BASR); - - // Non-JNI calls to native can have the post-deps added here. - // JNI dispatch has helper calls immedately after the BASR. The post-deps are appended to helpers. - if (jniCallDataSnippet == NULL) - cursor->setDependencyConditions(callPostDeps); + cursor->setDependencyConditions(callPostDeps); if (cg()->supportsJITFreeSystemStackPointer()) { @@ -499,20 +492,10 @@ TR::J9S390zOSSystemLinkage::addFECustomizedReturnRegDependency(int64_t killMask, return killMask; } + + /** - * \details - * A NOP padding is needed because returning from XPLINK functions - * skips the XPLink eyecatcher and always return to a point that's 2 or 4 bytes after the return address. - * - * In 64 bit XPLINK, the caller returns with a 'branch relative on condition' instruction with a 2 byte offset: - * - * - * 0x47F07002 B 2(,r7) - * - * In 31-bit XPLINK, this offset is 4-byte. - * - * As a result of this, JIT'ed code needs 2 or 4-byte NOP paddings to ensure entry to valid instruction. - * + * General XPLink utility */ TR::Instruction * TR::J9S390zOSSystemLinkage::genCallNOPAndDescriptor(TR::Instruction * cursor, diff --git a/runtime/compiler/z/codegen/J9S390SystemLinkage.hpp b/runtime/compiler/z/codegen/J9S390SystemLinkage.hpp index a3dd99b469a..6a14469a268 100644 --- a/runtime/compiler/z/codegen/J9S390SystemLinkage.hpp +++ b/runtime/compiler/z/codegen/J9S390SystemLinkage.hpp @@ -98,10 +98,6 @@ class J9S390zOSSystemLinkage: public TR::S390zOSSystemLinkage virtual int32_t storeExtraEnvRegForBuildArgs(TR::Node * callNode, TR::Linkage* linkage, TR::RegisterDependencyConditions * dependencies, bool isFastJNI, int32_t stackOffset, int8_t gprSize, uint32_t &numIntegerArgs); - /** - * \brief - * Insert NOP padding and call descriptor pseudo instruction after a XPLINK call. - */ TR::Instruction * genCallNOPAndDescriptor(TR::Instruction * cursor, TR::Node *node, TR::Node *callNode, TR_XPLinkCallTypes callType); }; diff --git a/runtime/oti/VMHelpers.hpp b/runtime/oti/VMHelpers.hpp index b94d26d8e0e..323e239fbc2 100644 --- a/runtime/oti/VMHelpers.hpp +++ b/runtime/oti/VMHelpers.hpp @@ -1134,10 +1134,9 @@ class VM_VMHelpers * currentThread must be up-to-date. * * @param[in] currentThread the current J9VMThread - * @param[in] J9Method the J9Method pointer of the JNI native function */ static VMINLINE void - beforeJNICall(J9VMThread *currentThread, J9Method* method) + beforeJNICall(J9VMThread *currentThread) { #if defined(J9VM_OPT_JAVA_OFFLOAD_SUPPORT) J9JavaVM *vm = currentThread->javaVM; @@ -1147,6 +1146,8 @@ class VM_VMHelpers /* save old state */ els->savedJavaOffloadState = currentThread->javaOffloadState; /* check if we need to change state */ + J9SFJNINativeMethodFrame *nativeMethodFrame = (J9SFJNINativeMethodFrame*)((UDATA)currentThread->sp + (UDATA)currentThread->literals); + J9Method *method = nativeMethodFrame->method; if (J9_ARE_ANY_BITS_SET((UDATA)method->constantPool, J9_STARTPC_NATIVE_REQUIRES_SWITCHING)) { /* zero the state and switch java offload mode OFF */ currentThread->javaOffloadState = 0; @@ -1168,10 +1169,9 @@ class VM_VMHelpers * currentThread must be up-to-date. * * @param[in] currentThread the current J9VMThread - * @param[in] J9Method the J9Method pointer of the JNI native function */ static VMINLINE void - afterJNICall(J9VMThread *currentThread, J9Method* method) + afterJNICall(J9VMThread *currentThread) { #if defined(J9VM_OPT_JAVA_OFFLOAD_SUPPORT) J9JavaVM *vm = currentThread->javaVM; @@ -1182,6 +1182,8 @@ class VM_VMHelpers if (0 == currentThread->javaOffloadState) { if (0 != els->savedJavaOffloadState) { /* if yes, call the offload switch ON and restore our state from ELS */ + J9SFJNINativeMethodFrame *nativeMethodFrame = (J9SFJNINativeMethodFrame*)((UDATA)currentThread->sp + (UDATA)currentThread->literals); + J9Method *method = nativeMethodFrame->method; vm->javaOffloadSwitchOnWithMethodFunc(currentThread, method); currentThread->javaOffloadState = els->savedJavaOffloadState; } diff --git a/runtime/vm/BytecodeInterpreter.hpp b/runtime/vm/BytecodeInterpreter.hpp index d6349fb1382..0a3386a59d5 100644 --- a/runtime/vm/BytecodeInterpreter.hpp +++ b/runtime/vm/BytecodeInterpreter.hpp @@ -2099,7 +2099,7 @@ obj:; * in the old stack, so does not need to be updated here. */ - ret = callCFunction(REGISTER_ARGS, jniMethodStartAddress, receiverAddress, javaArgs, &bp, isStatic, &returnType, _sendMethod); + ret = callCFunction(REGISTER_ARGS, jniMethodStartAddress, receiverAddress, javaArgs, &bp, isStatic, &returnType); if (isSynchronized) { j9object_t syncObject = NULL; @@ -2193,13 +2193,13 @@ done:; } VMINLINE FFI_Return - callCFunction(REGISTER_ARGS_LIST, void * jniMethodStartAddress, void *receiverAddress, UDATA *javaArgs, UDATA **bp, bool isStatic, U_8 *returnType, J9Method* method) + callCFunction(REGISTER_ARGS_LIST, void * jniMethodStartAddress, void *receiverAddress, UDATA *javaArgs, UDATA **bp, bool isStatic, U_8 *returnType) { /* Release VM access (all object pointers are indirect referenced from here on) */ UDATA relativeBP = _arg0EA - *bp; updateVMStruct(REGISTER_ARGS); VM_VMAccess::inlineExitVMToJNI(_currentThread); - VM_VMHelpers::beforeJNICall(_currentThread, method); + VM_VMHelpers::beforeJNICall(_currentThread); #if defined(J9VM_PORT_ZOS_CEEHDLRSUPPORT) if (J9_ARE_ANY_BITS_SET(_vm->sigFlags, J9_SIG_ZOS_CEEHDLR)) { _currentThread->tempSlot = (UDATA)jniMethodStartAddress; @@ -2217,7 +2217,7 @@ done:; } } #endif /* J9VM_PORT_ZOS_CEEHDLRSUPPORT */ - VM_VMHelpers::afterJNICall(_currentThread, method); + VM_VMHelpers::afterJNICall(_currentThread); /* Reacquire VM access (all object pointers can be direct referenced from this point on) */ VM_VMAccess::inlineEnterVMFromJNI(_currentThread); VMStructHasBeenUpdated(REGISTER_ARGS);