Path: blob/master/runtime/compiler/x/i386/codegen/IA32JNILinkage.cpp
6004 views
/*******************************************************************************1* Copyright (c) 2000, 2021 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* 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-exception20*******************************************************************************/2122#if defined(TR_TARGET_32BIT)2324#include "codegen/IA32JNILinkage.hpp"2526#include "codegen/CodeGenerator.hpp"27#include "codegen/Linkage.hpp"28#include "codegen/Linkage_inlines.hpp"29#include "codegen/MemoryReference.hpp"30#include "codegen/RealRegister.hpp"31#include "codegen/Register.hpp"32#include "codegen/RegisterPair.hpp"33#include "codegen/Snippet.hpp"34#include "compile/ResolvedMethod.hpp"35#include "env/jittypes.h"36#include "env/CompilerEnv.hpp"37#include "il/LabelSymbol.hpp"38#include "il/MethodSymbol.hpp"39#include "il/Node.hpp"40#include "il/Node_inlines.hpp"41#include "il/RegisterMappedSymbol.hpp"42#include "il/ResolvedMethodSymbol.hpp"43#include "il/StaticSymbol.hpp"44#include "il/Symbol.hpp"45#include "il/SymbolReference.hpp"46#include "env/VMJ9.h"47#include "runtime/Runtime.hpp"48#include "x/codegen/CheckFailureSnippet.hpp"49#include "x/codegen/IA32LinkageUtils.hpp"50#include "x/codegen/X86Instruction.hpp"515253TR::Register *J9::X86::I386::JNILinkage::buildDirectDispatch(TR::Node *callNode, bool spillFPRegs)54{55TR::MethodSymbol* methodSymbol = callNode->getSymbolReference()->getSymbol()->castToMethodSymbol();56TR_ASSERT(methodSymbol->isJNI(), "J9::X86::I386::JNILinkage::buildDirectDispatch can't hanlde this case.\n");57return buildJNIDispatch(callNode);58}5960TR::Register *J9::X86::I386::JNILinkage::buildJNIDispatch(TR::Node *callNode)61{62#ifdef DEBUG63if (debug("reportJNI"))64{65printf("JNI Dispatch: %s calling %s\n", comp()->signature(), comp()->getDebug()->getName(callNode->getSymbolReference()));66}67#endif6869TR::RegisterDependencyConditions *deps = generateRegisterDependencyConditions((uint8_t)0, 20, cg());7071TR::SymbolReference *callSymRef = callNode->getSymbolReference();72TR::MethodSymbol *callSymbol = callSymRef->getSymbol()->castToMethodSymbol();73TR::ResolvedMethodSymbol *resolvedMethodSymbol = callSymbol->castToResolvedMethodSymbol();74TR_ResolvedMethod *resolvedMethod = resolvedMethodSymbol->getResolvedMethod();75TR_J9VMBase *fej9 = (TR_J9VMBase *)(fe());76bool dropVMAccess = !fej9->jniRetainVMAccess(resolvedMethod);77bool isJNIGCPoint = !fej9->jniNoGCPoint(resolvedMethod);78bool killNonVolatileGPRs = isJNIGCPoint;79bool checkExceptions = !fej9->jniNoExceptionsThrown(resolvedMethod);80bool createJNIFrame = !fej9->jniNoNativeMethodFrame(resolvedMethod);81bool tearDownJNIFrame = !fej9->jniNoSpecialTeardown(resolvedMethod);82bool wrapRefs = !fej9->jniDoNotWrapObjects(resolvedMethod);83bool passReceiver = !fej9->jniDoNotPassReceiver(resolvedMethod);84bool passThread = !fej9->jniDoNotPassThread(resolvedMethod);8586if (resolvedMethodSymbol->canDirectNativeCall())87{88dropVMAccess = false;89killNonVolatileGPRs = false;90isJNIGCPoint = false;91checkExceptions = false;92createJNIFrame = false;93tearDownJNIFrame = false;94}95else if (callSymbol->isPureFunction())96{97dropVMAccess = false;98isJNIGCPoint = false;99checkExceptions = false;100}101102TR::Register* ebpReal = cg()->getVMThreadRegister();103TR::RealRegister* espReal = cg()->machine()->getRealRegister(TR::RealRegister::esp);104TR::LabelSymbol* returnAddrLabel = NULL;105106// Build parameters107int32_t argSize = buildParametersOnCStack(callNode, passReceiver ? 0 : 1, passThread, true);108109// JNI Call110TR::LabelSymbol* begLabel = generateLabelSymbol(cg());111TR::LabelSymbol* endLabel = generateLabelSymbol(cg());112begLabel->setStartInternalControlFlow();113endLabel->setEndInternalControlFlow();114115generateLabelInstruction(TR::InstOpCode::label, callNode, begLabel, cg());116117// Save VFP118TR::X86VFPSaveInstruction* vfpSave = generateVFPSaveInstruction(callNode, cg());119120returnAddrLabel = generateLabelSymbol(cg());121if (createJNIFrame)122{123// Anchored frame pointer register.124//125TR::RealRegister *ebxReal = cg()->machine()->getRealRegister(TR::RealRegister::ebx);126127128// Begin: mask out the magic bit that indicates JIT frames below129//130generateMemImmInstruction(131TR::InstOpCode::S4MemImm4,132callNode,133generateX86MemoryReference(ebpReal, fej9->thisThreadGetJavaFrameFlagsOffset(), cg()),1340,135cg()136);137// End: mask out the magic bit that indicates JIT frames below138139ebxReal->setHasBeenAssignedInMethod(true);140141// Build stack frame in java stack.142// Tag current bp.143//144uint32_t tagBits = fej9->constJNICallOutFrameSpecialTag();145146// If the current method is simply a wrapper for the JNI call, hide the call-out stack frame.147//148if (resolvedMethod == comp()->getCurrentMethod())149{150tagBits |= fej9->constJNICallOutFrameInvisibleTag();151}152153// Push tag bits (savedA0 slot).154//155generateImmInstruction(TR::InstOpCode::PUSHImm4, callNode, tagBits, cg());156157// Allocate space to get to frame size for special frames (skip savedPC slot).158//159generateRegImmInstruction(TR::InstOpCode::SUB4RegImms, callNode, espReal, 4, cg());160161// Push return address in this frame (savedCP slot).162//163TR::X86LabelInstruction* returnAddrLabelInstruction = generateLabelInstruction(TR::InstOpCode::PUSHImm4, callNode, returnAddrLabel, cg());164returnAddrLabelInstruction->setReloType(TR_AbsoluteMethodAddress);165166// Push frame flags.167//168generateImmInstruction(TR::InstOpCode::PUSHImm4, callNode, fej9->constJNICallOutFrameFlags(), cg());169170// Push the RAM method for the native.171//172static const TR_ExternalRelocationTargetKind reloTypes[] = {TR_VirtualRamMethodConst, TR_NoRelocation /*Interfaces*/, TR_StaticRamMethodConst, TR_SpecialRamMethodConst};173int rType = resolvedMethodSymbol->getMethodKind()-1; //method kinds are 1-based174TR_ASSERT(reloTypes[rType] != TR_NoRelocation, "There shouldn't be direct JNI interface calls!");175generateImmInstruction(TR::InstOpCode::PUSHImm4, callNode, (uintptr_t) resolvedMethod->resolvedMethodAddress(), cg(), reloTypes[rType]);176177// Store out pc and literals values indicating the callout frame.178//179generateMemImmInstruction(180TR::InstOpCode::S4MemImm4,181callNode,182generateX86MemoryReference(ebpReal, fej9->thisThreadGetJavaPCOffset(), cg()),183fej9->constJNICallOutFrameType(),184cg()185);186generateMemImmInstruction(187TR::InstOpCode::S4MemImm4,188callNode,189generateX86MemoryReference(ebpReal, fej9->thisThreadGetJavaLiteralsOffset(), cg()),1900,191cg()192);193}194195// Store out jsp.196//197generateMemRegInstruction(198TR::InstOpCode::S4MemReg,199callNode,200generateX86MemoryReference(ebpReal, fej9->thisThreadGetJavaSPOffset(), cg()),201espReal,202cg()203);204205// Switch stacks.206// Load up machine SP.207//208generateRegMemInstruction(209TR::InstOpCode::L4RegMem,210callNode,211espReal,212generateX86MemoryReference(ebpReal, ((TR_J9VMBase *) fej9)->thisThreadGetMachineSPOffset(), cg()),213cg()214);215216// Save ESP to EDI, a callee preserved register217// It is necessary because the JNI method may be either caller-cleanup or callee-cleanup218TR::Register *ediReal = cg()->allocateRegister();219generateRegRegInstruction(TR::InstOpCode::MOV4RegReg, callNode, ediReal, espReal, cg());220generateRegImmInstruction(argSize >= -128 && argSize <= 127 ? TR::InstOpCode::SUB4RegImms : TR::InstOpCode::SUB4RegImm4, callNode, espReal, argSize, cg());221222// We have to be careful to allocate the return register after the223// dependency conditions for the other killed registers have been set up,224// otherwise it will be marked as interfering with them.225// Start by creating a dummy post condition and then fill it in after the226// others have been set up.227//228deps->addPostCondition(NULL, TR::RealRegister::ecx, cg());229230TR::Register *esiReal;231TR::Register *eaxReal;232TR::Register *edxReal;233TR::Register *ebxReal;234eaxReal = cg()->allocateRegister();235deps->addPostCondition(eaxReal, TR::RealRegister::eax, cg());236cg()->stopUsingRegister(eaxReal);237238edxReal = cg()->allocateRegister();239deps->addPostCondition(NULL, TR::RealRegister::edx, cg());240241ebxReal = cg()->allocateRegister();242deps->addPostCondition(ebxReal, TR::RealRegister::ebx, cg());243cg()->stopUsingRegister(ebxReal);244245deps->addPostCondition(ediReal, TR::RealRegister::edi, cg());246247esiReal = cg()->allocateRegister();248deps->addPostCondition(esiReal, TR::RealRegister::esi, cg());249cg()->stopUsingRegister(esiReal);250251deps->getPostConditions()->setDependencyInfo(2, edxReal, TR::RealRegister::edx, cg());252if (!callNode->getType().isInt64())253cg()->stopUsingRegister(edxReal);254255TR::Register *ecxReal;256if (callNode->getDataType() == TR::Address)257ecxReal = cg()->allocateCollectedReferenceRegister();258else259ecxReal = cg()->allocateRegister();260deps->getPostConditions()->setDependencyInfo(0, ecxReal, TR::RealRegister::ecx, cg());261if (callNode->getDataType() == TR::NoType)262cg()->stopUsingRegister(ecxReal);263264// Kill all the xmm registers across the JNI callout sequence.265//266for (int i = 0; i <= 7; i++)267{268TR::Register *xmm_i = cg()->allocateRegister(TR_FPR);269deps->addPostCondition(xmm_i, TR::RealRegister::xmmIndex(i), cg());270cg()->stopUsingRegister(xmm_i);271}272273if (dropVMAccess)274{275generateMemImmInstruction(TR::InstOpCode::S4MemImm4,276callNode,277generateX86MemoryReference(ebpReal, offsetof(struct J9VMThread, inNative), cg()),2781,279cg());280281#if !defined(J9VM_INTERP_ATOMIC_FREE_JNI_USES_FLUSH)282TR::MemoryReference *mr = generateX86MemoryReference(espReal, intptr_t(0), cg());283mr->setRequiresLockPrefix();284generateMemImmInstruction(TR::InstOpCode::OR4MemImms, callNode, mr, 0, cg());285#endif /* !J9VM_INTERP_ATOMIC_FREE_JNI_USES_FLUSH */286287TR::LabelSymbol *longReleaseSnippetLabel = generateLabelSymbol(cg());288TR::LabelSymbol *longReleaseRestartLabel = generateLabelSymbol(cg());289290static_assert(J9_PUBLIC_FLAGS_VM_ACCESS <= 0x7fffffff, "VM access bit must be immediate");291generateMemImmInstruction(TR::InstOpCode::CMP4MemImm4,292callNode,293generateX86MemoryReference(ebpReal, fej9->thisThreadGetPublicFlagsOffset(), cg()),294J9_PUBLIC_FLAGS_VM_ACCESS,295cg());296generateLabelInstruction(TR::InstOpCode::JNE4, callNode, longReleaseSnippetLabel, cg());297generateLabelInstruction(TR::InstOpCode::label, callNode, longReleaseRestartLabel, cg());298299TR_OutlinedInstructionsGenerator og(longReleaseSnippetLabel, callNode, cg());300auto helper = comp()->getSymRefTab()->findOrCreateReleaseVMAccessSymbolRef(comp()->getMethodSymbol());301generateImmSymInstruction(TR::InstOpCode::CALLImm4, callNode, (uintptr_t)helper->getMethodAddress(), helper, cg());302generateLabelInstruction(TR::InstOpCode::JMP4, callNode, longReleaseRestartLabel, cg());303og.endOutlinedInstructionSequence();304}305306// Dispatch jni method directly.307//308TR::Instruction *instr = generateImmSymInstruction(309TR::InstOpCode::CALLImm4,310callNode,311(uintptr_t)resolvedMethodSymbol->getResolvedMethod()->startAddressForJNIMethod(comp()),312callNode->getSymbolReference(),313cg()314);315316if (isJNIGCPoint)317instr->setNeedsGCMap((argSize<<14) | 0xFF00FFFF);318319// memoize the call instruction, in order that we can register an assumption for this later on320cg()->getJNICallSites().push_front(new (trHeapMemory()) TR_Pair<TR_ResolvedMethod,TR::Instruction>(resolvedMethodSymbol->getResolvedMethod(), instr));321322// To Do:: will need an aot relocation for this one at some point.323324// Lay down a label for the frame push to reference.325//326generateLabelInstruction(TR::InstOpCode::label, callNode, returnAddrLabel, cg());327328// Restore stack pointer329generateRegRegInstruction(TR::InstOpCode::MOV4RegReg, callNode, espReal, ediReal, cg());330cg()->stopUsingRegister(ediReal);331332// Need to squirrel away the return value for some data types to avoid register333// conflicts with subsequent code to acquire vm access, etc.334//335// jni methods may not return a full register in some cases so need to get the declared336// type so that we sign and zero extend the narrower integer return types properly.337//338bool isUnsigned = resolvedMethod->returnTypeIsUnsigned();339bool isBoolean;340switch (resolvedMethod->returnType())341{342case TR::Int8:343isBoolean = comp()->getSymRefTab()->isReturnTypeBool(callSymRef);344if (isBoolean)345{346// For bool return type, must check whether value returned by347// JNI is zero (false) or non-zero (true) to yield Java result348generateRegRegInstruction(TR::InstOpCode::TEST1RegReg, callNode, eaxReal, eaxReal, cg());349generateRegInstruction(TR::InstOpCode::SETNE1Reg, callNode, eaxReal, cg());350}351generateRegRegInstruction((isUnsigned || isBoolean)352? TR::InstOpCode::MOVZXReg4Reg1 : TR::InstOpCode::MOVSXReg4Reg1,353callNode, ecxReal, eaxReal, cg());354break;355case TR::Int16:356generateRegRegInstruction(isUnsigned ? TR::InstOpCode::MOVZXReg4Reg2 : TR::InstOpCode::MOVSXReg4Reg2,357callNode, ecxReal, eaxReal, cg());358break;359case TR::Address:360case TR::Int64:361case TR::Int32:362generateRegRegInstruction(TR::InstOpCode::MOV4RegReg, callNode, ecxReal, eaxReal, cg());363break;364}365366if (dropVMAccess)367{368// Re-acquire vm access.369//370generateMemImmInstruction(TR::InstOpCode::S4MemImm4,371callNode,372generateX86MemoryReference(ebpReal, offsetof(struct J9VMThread, inNative), cg()),3730,374cg());375376#if !defined(J9VM_INTERP_ATOMIC_FREE_JNI_USES_FLUSH)377TR::MemoryReference *mr = generateX86MemoryReference(espReal, intptr_t(0), cg());378mr->setRequiresLockPrefix();379generateMemImmInstruction(TR::InstOpCode::OR4MemImms, callNode, mr, 0, cg());380#endif /* !J9VM_INTERP_ATOMIC_FREE_JNI_USES_FLUSH */381382TR::LabelSymbol *longAcquireSnippetLabel = generateLabelSymbol(cg());383TR::LabelSymbol *longAcquireRestartLabel = generateLabelSymbol(cg());384385static_assert(J9_PUBLIC_FLAGS_VM_ACCESS <= 0x7fffffff, "VM access bit must be immediate");386generateMemImmInstruction(TR::InstOpCode::CMP4MemImm4,387callNode,388generateX86MemoryReference(ebpReal, fej9->thisThreadGetPublicFlagsOffset(), cg()),389J9_PUBLIC_FLAGS_VM_ACCESS,390cg());391generateLabelInstruction(TR::InstOpCode::JNE4, callNode, longAcquireSnippetLabel, cg());392generateLabelInstruction(TR::InstOpCode::label, callNode, longAcquireRestartLabel, cg());393394TR_OutlinedInstructionsGenerator og(longAcquireSnippetLabel, callNode, cg());395auto helper = comp()->getSymRefTab()->findOrCreateAcquireVMAccessSymbolRef(comp()->getMethodSymbol());396generateImmSymInstruction(TR::InstOpCode::CALLImm4, callNode, (uintptr_t)helper->getMethodAddress(), helper, cg());397generateLabelInstruction(TR::InstOpCode::JMP4, callNode, longAcquireRestartLabel, cg());398og.endOutlinedInstructionSequence();399}400401if (TR::Address == resolvedMethod->returnType() && wrapRefs)402{403// Unless NULL, need to indirect once to get the real java reference404//405TR::LabelSymbol *tempLab = generateLabelSymbol(cg());406generateRegRegInstruction(TR::InstOpCode::TEST4RegReg, callNode, ecxReal, ecxReal, cg());407generateLabelInstruction(TR::InstOpCode::JE4, callNode, tempLab, cg());408generateRegMemInstruction(TR::InstOpCode::L4RegMem, callNode, ecxReal, generateX86MemoryReference(ecxReal, 0, cg()), cg());409generateLabelInstruction(TR::InstOpCode::label, callNode, tempLab, cg());410}411412// Switch stacks back.413// First store out the machine sp into the vm thread. It has to be done as sometimes414// it gets tromped on by call backs.415//416generateMemRegInstruction(417TR::InstOpCode::S4MemReg,418callNode,419generateX86MemoryReference(ebpReal, fej9->thisThreadGetMachineSPOffset(), cg()),420espReal,421cg()422);423424// Next load up the java sp so we have the callout frame on top of the java stack.425//426generateRegMemInstruction(427TR::InstOpCode::L4RegMem,428callNode,429espReal,430generateX86MemoryReference(ebpReal, fej9->thisThreadGetJavaSPOffset(), cg()),431cg()432);433434if (createJNIFrame)435{436generateRegMemInstruction(437TR::InstOpCode::ADD4RegMem,438callNode,439espReal,440generateX86MemoryReference(ebpReal, fej9->thisThreadGetJavaLiteralsOffset(), cg()),441cg()442);443444if (tearDownJNIFrame)445{446// Must check to see if the ref pool was used and clean them up if so--or we447// leave a bunch of pinned garbage behind that screws up the gc quality forever.448//449uint32_t flagValue = fej9->constJNIReferenceFrameAllocatedFlags();450TR::InstOpCode::Mnemonic op = flagValue <= 255 ? TR::InstOpCode::TEST1MemImm1 : TR::InstOpCode::TEST4MemImm4;451TR::LabelSymbol *refPoolSnippetLabel = generateLabelSymbol(cg());452TR::LabelSymbol *refPoolRestartLabel = generateLabelSymbol(cg());453generateMemImmInstruction(op, callNode, generateX86MemoryReference(espReal, fej9->constJNICallOutFrameFlagsOffset(), cg()), flagValue, cg());454generateLabelInstruction(TR::InstOpCode::JNE4, callNode, refPoolSnippetLabel, cg());455generateLabelInstruction(TR::InstOpCode::label, callNode, refPoolRestartLabel, cg());456457TR_OutlinedInstructionsGenerator og(refPoolSnippetLabel, callNode, cg());458generateHelperCallInstruction(callNode, TR_IA32jitCollapseJNIReferenceFrame, NULL, cg());459generateLabelInstruction(TR::InstOpCode::JMP4, callNode, refPoolRestartLabel, cg());460og.endOutlinedInstructionSequence();461}462463// Now set esp back to its previous value.464//465generateRegImmInstruction(TR::InstOpCode::ADD4RegImms, callNode, espReal, 20, cg());466}467468// Get return registers set up before preserved regs are restored.469//470TR::Register *returnRegister = NULL;471switch (callNode->getDataType())472{473case TR::Int32:474case TR::Address:475returnRegister = ecxReal;476break;477case TR::Int64:478returnRegister = cg()->allocateRegisterPair(ecxReal, edxReal);479break;480481// Current system linkage does not use XMM0 for floating point return, even if SSE is supported on the processor.482//483case TR::Float:484deps->addPostCondition(returnRegister = cg()->allocateSinglePrecisionRegister(TR_X87), TR::RealRegister::st0, cg());485break;486case TR::Double:487deps->addPostCondition(returnRegister = cg()->allocateRegister(TR_X87), TR::RealRegister::st0, cg());488break;489}490491deps->stopAddingConditions();492493if (checkExceptions)494{495// Check exceptions.496//497generateMemImmInstruction(498TR::InstOpCode::CMP4MemImms,499callNode,500generateX86MemoryReference(ebpReal, fej9->thisThreadGetCurrentExceptionOffset(), cg()),5010,502cg()503);504TR::LabelSymbol *snippetLabel = generateLabelSymbol(cg());505instr = generateLabelInstruction(TR::InstOpCode::JNE4, callNode, snippetLabel, cg());506instr->setNeedsGCMap((argSize<<14) | 0xFF00FFFF);507508TR::Snippet *snippet = new (trHeapMemory()) TR::X86CheckFailureSnippet(509cg(),510cg()->symRefTab()->findOrCreateRuntimeHelper(TR_throwCurrentException),511snippetLabel,512instr,513callNode->getDataType() == TR::Float || callNode->getDataType() == TR::Double);514cg()->addSnippet(snippet);515}516517// Restore VFP518generateVFPRestoreInstruction(vfpSave, callNode, cg());519generateLabelInstruction(TR::InstOpCode::label, callNode, endLabel, deps, cg());520521// Stop using the killed registers that are not going to persist.522//523if (deps)524stopUsingKilledRegisters(deps, returnRegister);525526// If the processor supports SSE, return floating-point values in XMM registers.527//528if (callNode->getOpCode().isFloat())529{530TR::MemoryReference *tempMR = cg()->machine()->getDummyLocalMR(TR::Float);531generateFPMemRegInstruction(TR::InstOpCode::FSTPMemReg, callNode, tempMR, returnRegister, cg());532returnRegister = cg()->allocateSinglePrecisionRegister(TR_FPR);533generateRegMemInstruction(TR::InstOpCode::MOVSSRegMem, callNode, returnRegister, generateX86MemoryReference(*tempMR, 0, cg()), cg());534}535else if (callNode->getOpCode().isDouble())536{537TR::MemoryReference *tempMR = cg()->machine()->getDummyLocalMR(TR::Double);538generateFPMemRegInstruction(TR::InstOpCode::DSTPMemReg, callNode, tempMR, returnRegister, cg());539returnRegister = cg()->allocateRegister(TR_FPR);540generateRegMemInstruction(cg()->getXMMDoubleLoadOpCode(), callNode, returnRegister, generateX86MemoryReference(*tempMR, 0, cg()), cg());541}542543if (cg()->enableRegisterAssociations())544associatePreservedRegisters(deps, returnRegister);545546return returnRegister;547}548549#endif550551552