Path: blob/master/runtime/compiler/x/amd64/codegen/AMD64JNILinkage.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#ifdef TR_TARGET_64BIT2324#include "codegen/AMD64JNILinkage.hpp"2526#include <algorithm>27#include "codegen/CodeGenerator.hpp"28#include "codegen/Linkage_inlines.hpp"29#include "codegen/Machine.hpp"30#include "codegen/MemoryReference.hpp"31#include "codegen/RealRegister.hpp"32#include "codegen/Register.hpp"33#include "codegen/RegisterDependency.hpp"34#include "codegen/Snippet.hpp"35#include "compile/ResolvedMethod.hpp"36#include "env/CompilerEnv.hpp"37#include "env/jittypes.h"38#include "il/LabelSymbol.hpp"39#include "il/MethodSymbol.hpp"40#include "il/Node.hpp"41#include "il/Node_inlines.hpp"42#include "il/RegisterMappedSymbol.hpp"43#include "il/ResolvedMethodSymbol.hpp"44#include "il/StaticSymbol.hpp"45#include "il/Symbol.hpp"46#include "il/SymbolReference.hpp"47#include "infra/Assert.hpp"48#include "env/VMJ9.h"49#include "runtime/Runtime.hpp"50#include "x/codegen/CheckFailureSnippet.hpp"51#include "x/codegen/HelperCallSnippet.hpp"52#include "x/codegen/X86Instruction.hpp"53#include "x/codegen/J9LinkageUtils.hpp"5455TR::Register *J9::X86::AMD64::JNILinkage::processJNIReferenceArg(TR::Node *child)56{57TR::Register *refReg;5859if (child->getOpCodeValue() == TR::loadaddr)60{61TR::SymbolReference *symRef = child->getSymbolReference();62TR::StaticSymbol *staticSym = symRef->getSymbol()->getStaticSymbol();63bool needsNullParameterCheck = false;6465if (staticSym)66{67// Address taken of static.68//69refReg = cg()->evaluate(child);70if (!staticSym->isAddressOfClassObject())71needsNullParameterCheck = true;72}73else74{75// Address taken of a parm or local.76//77if (child->pointsToNull())78{79refReg = cg()->allocateRegister();80generateRegRegInstruction(TR::InstOpCode::XORRegReg(), child, refReg, refReg, cg());81// TODO (81564): We need to kill the scratch register to prevent an82// assertion error, but is this the right place to do so?83cg()->stopUsingRegister(refReg);84}85else86{87refReg = cg()->evaluate(child);88if (!child->pointsToNonNull())89needsNullParameterCheck = true;90}91}9293if (needsNullParameterCheck)94{95generateMemImmInstruction(TR::InstOpCode::CMPMemImms(), child, generateX86MemoryReference(refReg, 0, cg()), 0, cg());96generateRegMemInstruction(TR::InstOpCode::CMOVERegMem(), child, refReg, generateX86MemoryReference(cg()->findOrCreateConstantDataSnippet<intptr_t>(child, 0), cg()), cg());97}98}99else100{101refReg = cg()->evaluate(child);102}103104return refReg;105}106107108int32_t J9::X86::AMD64::JNILinkage::computeMemoryArgSize(109TR::Node *callNode,110int32_t first,111int32_t last,112bool passThread)113{114int32_t size= 0;115int32_t numFloatArgs = 0;116int32_t numIntArgs = 0;117118int32_t slotSize = TR::Compiler->om.sizeofReferenceAddress();119int32_t slotSizeMinus1 = slotSize-1;120121// For JNI dispatch, count the JNI env pointer first. It is122// assumed that for the 64-bit register passing linkages for JNI123// that the evaluation order is left-to-right and that the JNI env124// pointer will be in a register as the first argument.125//126if (passThread)127{128TR_ASSERT(!_systemLinkage->getProperties().passArgsRightToLeft(), "JNI argument evaluation expected to be left-to-right");129TR_ASSERT(_systemLinkage->getProperties().getNumIntegerArgumentRegisters() > 0, "JNI env pointer expected to be passed in a register.");130numIntArgs++;131132if (_systemLinkage->getProperties().getLinkageRegistersAssignedByCardinalPosition())133numFloatArgs++;134}135136bool allocateOnCallerFrame = false;137138for (int32_t i = first; i != last; i++)139{140TR::Node *child = callNode->getChild(i);141TR::DataType type = child->getDataType();142143switch (type)144{145case TR::Float:146case TR::Double:147if (numFloatArgs >= _systemLinkage->getProperties().getNumFloatArgumentRegisters())148allocateOnCallerFrame = true;149numFloatArgs++;150151if (_systemLinkage->getProperties().getLinkageRegistersAssignedByCardinalPosition())152numIntArgs++;153154break;155156case TR::Address:157default:158if (numIntArgs >= _systemLinkage->getProperties().getNumIntegerArgumentRegisters())159allocateOnCallerFrame = true;160numIntArgs++;161162if (_systemLinkage->getProperties().getLinkageRegistersAssignedByCardinalPosition())163numFloatArgs++;164165break;166}167168if (allocateOnCallerFrame)169{170int32_t roundedSize = (child->getSize()+slotSizeMinus1)&(~slotSizeMinus1);171size += roundedSize ? roundedSize : slotSize;172allocateOnCallerFrame = false;173}174}175176// Always reserve space on the caller's frame for the maximum number of linkage registers.177//178if (_systemLinkage->getProperties().getCallerFrameAllocatesSpaceForLinkageRegisters())179{180// TODO: this is Win64 Fastcall-specific at the moment. It could be generalized if need be.181//182size += std::max(_systemLinkage->getProperties().getNumIntegerArgumentRegisters(),183_systemLinkage->getProperties().getNumFloatArgumentRegisters()) * TR::Compiler->om.sizeofReferenceAddress();184}185186return size;187}188189190// Build arguments for system linkage dispatch.191//192int32_t J9::X86::AMD64::JNILinkage::buildArgs(193TR::Node *callNode,194TR::RegisterDependencyConditions *deps,195bool passThread,196bool passReceiver)197{198TR::SymbolReference *methodSymRef = callNode->getSymbolReference();199TR::MethodSymbol *methodSymbol = methodSymRef->getSymbol()->castToMethodSymbol();200TR::Machine *machine = cg()->machine();201TR::RealRegister::RegNum noReg = TR::RealRegister::NoReg;202TR::RealRegister *espReal = machine->getRealRegister(TR::RealRegister::esp);203int32_t firstNodeArgument = callNode->getFirstArgumentIndex();204int32_t lastNodeArgument = callNode->getNumChildren() - 1;205int32_t offset = 0;206uint16_t numIntArgs = 0,207numFloatArgs = 0;208int32_t first, last;209210int32_t numCopiedRegs = 0;211TR::Register *copiedRegs[TR::X86LinkageProperties::MaxArgumentRegisters];212213TR_ASSERT(!_systemLinkage->getProperties().passArgsRightToLeft(), "JNI dispatch expected to be left-to-right.");214215if (!passReceiver)216first = firstNodeArgument + 1;217else first = firstNodeArgument;218last = lastNodeArgument + 1;219220// We need to know how many arguments will be passed on the stack in order to add the right221// amount of slush first for proper alignment.222//223int32_t memoryArgSize = computeMemoryArgSize(callNode, first, last, passThread);224225// For JNI callout there may be some extra information (such as the VMThread pointer)226// that has been preserved on the stack that must be accounted for in this calculation.227//228int32_t adjustedMemoryArgSize = memoryArgSize + _JNIDispatchInfo.argSize;229230if (adjustedMemoryArgSize > 0)231{232// The C stack pointer (RSP) is 16-aligned before building any memory arguments (guaranteed by the VM).233// Following the last memory argument the stack alignment must still be 16.234//235if ((adjustedMemoryArgSize % 16) != 0)236memoryArgSize += 8;237238TR::InstOpCode::Mnemonic op = (memoryArgSize >= -128 && memoryArgSize <= 127) ? TR::InstOpCode::SUBRegImms() : TR::InstOpCode::SUBRegImm4();239generateRegImmInstruction(op, callNode, espReal, memoryArgSize, cg());240}241242// Evaluate the JNI env pointer first.243//244if (passThread)245{246TR::RealRegister::RegNum rregIndex = _systemLinkage->getProperties().getIntegerArgumentRegister(0);247TR::Register *argReg = cg()->allocateRegister();248generateRegRegInstruction(TR::Linkage::movOpcodes(RegReg, movType(TR::Address)), callNode, argReg, cg()->getMethodMetaDataRegister(), cg());249deps->addPreCondition(argReg, rregIndex, cg());250cg()->stopUsingRegister(argReg);251numIntArgs++;252253if (_systemLinkage->getProperties().getLinkageRegistersAssignedByCardinalPosition())254numFloatArgs++;255256if (_systemLinkage->getProperties().getCallerFrameAllocatesSpaceForLinkageRegisters())257{258int32_t slotSize = TR::Compiler->om.sizeofReferenceAddress();259int32_t slotSizeMinus1 = slotSize-1;260261int32_t roundedSize = (slotSize+slotSizeMinus1)&(~slotSizeMinus1);262offset += roundedSize ? roundedSize : slotSize;263}264}265266// if fastJNI do not pass the receiver just evaluate the first child267if (!passReceiver)268{269TR::Node *child = callNode->getChild(first-1);270cg()->evaluate(child);271cg()->decReferenceCount(child);272}273274int32_t i;275for (i = first; i != last; i++)276{277TR::Node *child = callNode->getChild(i);278TR::DataType type = child->getDataType();279TR::RealRegister::RegNum rregIndex = noReg;280281switch (type)282{283case TR::Float:284case TR::Double:285rregIndex =286(numFloatArgs < _systemLinkage->getProperties().getNumFloatArgumentRegisters()) ? _systemLinkage->getProperties().getFloatArgumentRegister(numFloatArgs) : noReg;287numFloatArgs++;288289if (_systemLinkage->getProperties().getLinkageRegistersAssignedByCardinalPosition())290numIntArgs++;291292break;293294case TR::Address:295// Deliberate fall through for TR::Address types.296//297default:298rregIndex =299(numIntArgs < _systemLinkage->getProperties().getNumIntegerArgumentRegisters()) ? _systemLinkage->getProperties().getIntegerArgumentRegister(numIntArgs) : noReg;300numIntArgs++;301302if (_systemLinkage->getProperties().getLinkageRegistersAssignedByCardinalPosition())303numFloatArgs++;304305break;306}307308TR::Register *vreg;309if (type == TR::Address)310vreg = processJNIReferenceArg(child);311else312vreg = cg()->evaluate(child);313314bool needsStackOffsetUpdate = false;315if (rregIndex != noReg)316{317// For NULL JNI reference parameters, it is possible that the NULL value will be evaluated into318// a different register than the child. In that case it is not necessary to copy the temporary scratch319// register across the call.320//321if ((child->getReferenceCount() > 1) &&322(vreg == child->getRegister()))323{324TR::Register *argReg = cg()->allocateRegister();325if (vreg->containsCollectedReference())326argReg->setContainsCollectedReference();327generateRegRegInstruction(TR::Linkage::movOpcodes(RegReg, movType(child->getDataType())), child, argReg, vreg, cg());328vreg = argReg;329copiedRegs[numCopiedRegs++] = vreg;330}331332deps->addPreCondition(vreg, rregIndex, cg());333334if (_systemLinkage->getProperties().getCallerFrameAllocatesSpaceForLinkageRegisters())335needsStackOffsetUpdate = true;336}337else338{339// Ideally, we would like to push rather than move340generateMemRegInstruction(TR::Linkage::movOpcodes(MemReg, fullRegisterMovType(vreg)),341child,342generateX86MemoryReference(espReal, offset, cg()),343vreg,344cg());345346needsStackOffsetUpdate = true;347}348349if (needsStackOffsetUpdate)350{351int32_t slotSize = TR::Compiler->om.sizeofReferenceAddress();352int32_t slotSizeMinus1 = slotSize-1;353354int32_t roundedSize = (child->getSize()+slotSizeMinus1)&(~slotSizeMinus1);355offset += roundedSize ? roundedSize : slotSize;356}357358cg()->decReferenceCount(child);359}360361// Now that we're finished making the preconditions, all the interferences362// are established and we can kill these regs.363//364for (i = 0; i < numCopiedRegs; i++)365cg()->stopUsingRegister(copiedRegs[i]);366367deps->stopAddingPreConditions();368369return memoryArgSize;370}371372373TR::Register *374J9::X86::AMD64::JNILinkage::buildVolatileAndReturnDependencies(375TR::Node *callNode,376TR::RegisterDependencyConditions *deps,377bool omitDedicatedFrameRegister)378{379TR_ASSERT(deps != NULL, "expected register dependencies");380381// Figure out which is the return register.382//383TR::RealRegister::RegNum returnRegIndex;384TR_RegisterKinds returnKind;385386switch (callNode->getDataType())387{388default:389TR_ASSERT(0, "Unrecognized call node data type: #%d", (int)callNode->getDataType());390// deliberate fall-through391case TR::NoType:392returnRegIndex = TR::RealRegister::NoReg;393returnKind = TR_NoRegister;394break;395case TR::Int8:396case TR::Int16:397case TR::Int32:398case TR::Int64:399case TR::Address:400returnRegIndex = _systemLinkage->getProperties().getIntegerReturnRegister();401returnKind = TR_GPR;402break;403case TR::Float:404case TR::Double:405returnRegIndex = _systemLinkage->getProperties().getFloatReturnRegister();406returnKind = TR_FPR;407break;408}409410// Kill all non-preserved int and float regs besides the return register.411//412int32_t i;413TR::RealRegister::RegNum scratchIndex = _systemLinkage->getProperties().getIntegerScratchRegister(1);414for (i=0; i<_systemLinkage->getProperties().getNumVolatileRegisters(); i++)415{416TR::RealRegister::RegNum regIndex = _systemLinkage->getProperties()._volatileRegisters[i];417418if (regIndex != returnRegIndex)419{420if (!omitDedicatedFrameRegister || (regIndex != _JNIDispatchInfo.dedicatedFrameRegisterIndex))421{422TR_RegisterKinds rk = (i < _systemLinkage->getProperties()._numberOfVolatileGPRegisters) ? TR_GPR : TR_FPR;423TR::Register *dummy = cg()->allocateRegister(rk);424deps->addPostCondition(dummy, regIndex, cg());425426// Note that we don't setPlaceholderReg here. If this volatile reg is also volatile427// in the caller's linkage, then that flag doesn't matter much anyway. If it's preserved428// in the caller's linkage, then we don't want to set that flag because we want this429// use of the register to count as a "real" use, thereby motivating the prologue to430// preserve the regsiter.431432// A scratch register is necessary to call the native without a trampoline.433//434if (regIndex != scratchIndex)435cg()->stopUsingRegister(dummy);436}437}438}439440// Add a VMThread register dependence.441//442deps->addPostCondition(cg()->getVMThreadRegister(), TR::RealRegister::ebp, cg());443444// Now that everything is dead, we can allocate the return register without445// interference446//447TR::Register *returnRegister;448if (returnRegIndex)449{450TR_ASSERT(returnKind != TR_NoRegister, "assertion failure");451452if (callNode->getDataType() == TR::Address)453returnRegister = cg()->allocateCollectedReferenceRegister();454else455{456returnRegister = cg()->allocateRegister(returnKind);457if (callNode->getDataType() == TR::Float)458returnRegister->setIsSinglePrecision();459}460461deps->addPostCondition(returnRegister, returnRegIndex, cg());462}463else464returnRegister = NULL;465466deps->stopAddingPostConditions();467468return returnRegister;469}470471472void J9::X86::AMD64::JNILinkage::buildJNICallOutFrame(473TR::Node *callNode,474TR::LabelSymbol *returnAddrLabel)475{476TR::ResolvedMethodSymbol *callSymbol = callNode->getSymbol()->castToResolvedMethodSymbol();477TR_ResolvedMethod *resolvedMethod = callSymbol->getResolvedMethod();478TR::Register *vmThreadReg = cg()->getMethodMetaDataRegister();479TR::Register *scratchReg = NULL;480TR::RealRegister *espReal = machine()->getRealRegister(TR::RealRegister::esp);481482TR_J9VMBase *fej9 = (TR_J9VMBase *)(fe());483484// Mask out the magic bit that indicates JIT frames below.485//486generateMemImmInstruction(487TR::InstOpCode::SMemImm4(),488callNode,489generateX86MemoryReference(vmThreadReg, fej9->thisThreadGetJavaFrameFlagsOffset(), cg()),4900,491cg());492493// Grab 5 slots in the frame.494//495// 4: tag bits (savedA0)496// 3: empty (savedPC)497// 2: return address in this frame (savedCP)498// 1: frame flags499// 0: RAM method500//501502// Build stack frame in Java stack. Tag current bp.503//504uintptr_t tagBits = 0;505506// If the current method is simply a wrapper for the JNI call, hide the call-out stack frame.507//508static_assert(IS_32BIT_SIGNED(J9SF_A0_INVISIBLE_TAG), "J9SF_A0_INVISIBLE_TAG must fit in immediate");509if (resolvedMethod == comp()->getCurrentMethod())510tagBits |= J9SF_A0_INVISIBLE_TAG;511512// Push tag bits (savedA0 slot).513//514generateImmInstruction(TR::InstOpCode::PUSHImm4, callNode, tagBits, cg());515516// (skip savedPC slot).517//518generateImmInstruction(TR::InstOpCode::PUSHImm4, callNode, 0, cg());519520// Push return address in this frame (savedCP slot).521//522if (!scratchReg)523scratchReg = cg()->allocateRegister();524525TR::AMD64RegImm64SymInstruction* returnAddressInstr =526generateRegImm64SymInstruction(527TR::InstOpCode::MOV8RegImm64,528callNode,529scratchReg,5300,531new (trHeapMemory()) TR::SymbolReference(comp()->getSymRefTab(), returnAddrLabel),532cg());533534returnAddressInstr->setReloKind(TR_AbsoluteMethodAddress);535536generateRegInstruction(TR::InstOpCode::PUSHReg, callNode, scratchReg, cg());537538// Push frame flags.539//540static_assert(IS_32BIT_SIGNED(J9_SSF_JIT_JNI_CALLOUT), "J9_SSF_JIT_JNI_CALLOUT must fit in immediate");541generateImmInstruction(TR::InstOpCode::PUSHImm4, callNode, J9_SSF_JIT_JNI_CALLOUT, cg());542543// Push the RAM method for the native.544//545auto tempMR = generateX86MemoryReference(espReal, 0, cg());546uintptr_t methodAddr = (uintptr_t) resolvedMethod->resolvedMethodAddress();547if (IS_32BIT_SIGNED(methodAddr) && !TR::Compiler->om.nativeAddressesCanChangeSize())548{549generateImmInstruction(TR::InstOpCode::PUSHImm4, callNode, methodAddr, cg());550}551else552{553if (!scratchReg)554scratchReg = cg()->allocateRegister();555556static const TR_ExternalRelocationTargetKind reloTypes[] = { TR_VirtualRamMethodConst, TR_NoRelocation /*Interfaces*/, TR_StaticRamMethodConst, TR_SpecialRamMethodConst };557int reloType = callSymbol->getMethodKind() - 1; //method kinds are 1-based!!558TR_ASSERT(reloTypes[reloType] != TR_NoRelocation, "There shouldn't be direct JNI interface calls!");559generateRegImm64Instruction(TR::InstOpCode::MOV8RegImm64, callNode, scratchReg, methodAddr, cg(), reloTypes[reloType]);560generateRegInstruction(TR::InstOpCode::PUSHReg, callNode, scratchReg, cg());561}562563// Store out pc and literals values indicating the callout frame.564//565static_assert(IS_32BIT_SIGNED(J9SF_FRAME_TYPE_JIT_JNI_CALLOUT), "J9SF_FRAME_TYPE_JIT_JNI_CALLOUT must fit in immediate");566generateMemImmInstruction(TR::InstOpCode::SMemImm4(), callNode, generateX86MemoryReference(vmThreadReg, fej9->thisThreadGetJavaPCOffset(), cg()), J9SF_FRAME_TYPE_JIT_JNI_CALLOUT, cg());567568if (scratchReg)569cg()->stopUsingRegister(scratchReg);570571generateMemImmInstruction(TR::InstOpCode::SMemImm4(),572callNode,573generateX86MemoryReference(vmThreadReg, fej9->thisThreadGetJavaLiteralsOffset(), cg()),5740,575cg());576}577578void579J9::X86::AMD64::JNILinkage::buildJNIMergeLabelDependencies(TR::Node *callNode, bool killNonVolatileGPRs)580{581TR::RegisterDependencyConditions *deps = _JNIDispatchInfo.mergeLabelPostDeps;582583// Allocate the actual register that the JNI result will be returned in. This may be different584// than the linkage return register on the call because of register usage constraints after the585// call (e.g., re-acquiring the VM).586//587TR::RealRegister::RegNum returnRegIndex;588TR::Register *linkageReturnRegister = _JNIDispatchInfo.linkageReturnRegister;589TR::Register *JNIReturnRegister;590591if (linkageReturnRegister)592{593JNIReturnRegister = cg()->allocateRegister(linkageReturnRegister->getKind());594if (linkageReturnRegister->containsCollectedReference())595JNIReturnRegister->setContainsCollectedReference();596else if (linkageReturnRegister->isSinglePrecision())597JNIReturnRegister->setIsSinglePrecision();598599if (JNIReturnRegister->getKind() == TR_GPR)600returnRegIndex = TR::RealRegister::ecx;601else602returnRegIndex = _systemLinkage->getProperties().getFloatReturnRegister();603604deps->addPostCondition(JNIReturnRegister, returnRegIndex, cg());605}606else607{608JNIReturnRegister = NULL;609returnRegIndex = TR::RealRegister::NoReg;610}611612_JNIDispatchInfo.JNIReturnRegister = JNIReturnRegister;613614// Kill all registers across the JNI callout sequence.615//616int32_t i;617for (i=0; i<_systemLinkage->getProperties().getNumVolatileRegisters(); i++)618{619TR::RealRegister::RegNum regIndex = _systemLinkage->getProperties()._volatileRegisters[i];620621if (regIndex != returnRegIndex)622{623TR_RegisterKinds rk = (i < _systemLinkage->getProperties()._numberOfVolatileGPRegisters) ? TR_GPR : TR_FPR;624TR::Register *dummy = cg()->allocateRegister(rk);625626// Don't setPlaceholderReg here in case the caller's linkage lists this one as a preserved reg627//628deps->addPostCondition(dummy, regIndex, cg());629cg()->stopUsingRegister(dummy);630}631}632633if (killNonVolatileGPRs)634{635for (i=0; i<_systemLinkage->getProperties().getNumPreservedRegisters(); i++)636{637TR::RealRegister::RegNum regIndex = _systemLinkage->getProperties()._preservedRegisters[i];638639if (regIndex != returnRegIndex)640{641TR_RegisterKinds rk = (i < _systemLinkage->getProperties()._numberOfPreservedGPRegisters) ? TR_GPR : TR_FPR;642TR::Register *dummy = cg()->allocateRegister(rk);643644// Don't setPlaceholderReg here. We release VM access around the645// native call, so even though the callee's linkage won't alter a646// given register, we still have a problem if a stack walk needs to647// inspect/modify that register because once we're in C-land, we have648// no idea where that regsiter's value is located. Therefore, we need649// to spill even the callee-saved registers around the call.650//651// In principle, we only need to do this for registers that contain652// references. However, at this location in the code, we don't yet653// know which real registers those would be. Tragically, this causes654// us to save/restore ALL preserved registers in any method containing655// a JNI call.656657deps->addPostCondition(dummy, regIndex, cg());658cg()->stopUsingRegister(dummy);659}660}661}662663// Add a VMThread register dependence.664//665deps->addPostCondition(cg()->getVMThreadRegister(), TR::RealRegister::ebp, cg());666deps->stopAddingPostConditions();667}668669void J9::X86::AMD64::JNILinkage::buildOutgoingJNIArgsAndDependencies(670TR::Node *callNode,671bool passThread,672bool passReceiver,673bool killNonVolatileGPRs)674{675// Allocate adequate register dependencies. The pre and post conditions are distinguished because they will676// appear on different instructions.677//678// callPre = number of argument registers679// callPost = number of volatile + VMThread + return register680// labelPost = number of volatile + preserved + VMThread + return register681//682uint32_t callPre = _systemLinkage->getProperties().getNumIntegerArgumentRegisters() + _systemLinkage->getProperties().getNumFloatArgumentRegisters();683uint32_t callPost = _systemLinkage->getProperties().getNumVolatileRegisters() + 1 + (callNode->getDataType() == TR::NoType ? 0 : 1);684685// This is overly pessimistic. In reality, only the linkage preserved registers that contain collected references686// need to be evicted across the JNI call. There currently isn't a convenient mechanism to specify this...687//688uint32_t labelPost = _systemLinkage->getProperties().getNumVolatileRegisters() +689_systemLinkage->getProperties().getNumPreservedRegisters() + 1 +690(callNode->getDataType() == TR::NoType ? 0 : 1);691692_JNIDispatchInfo.callPostDeps = generateRegisterDependencyConditions(callPre, callPost, cg());693_JNIDispatchInfo.mergeLabelPostDeps = generateRegisterDependencyConditions(0, labelPost, cg());694695// Evaluate outgoing arguments on the system stack and build pre-conditions.696//697_JNIDispatchInfo.argSize += buildArgs(callNode, _JNIDispatchInfo.callPostDeps, passThread, passReceiver);698699// Build post-conditions.700//701// Omit the dedicated frame register because it will be in Locked state by the time702// the register assigner reaches the call instruction, and the assignment won't happen.703//704_JNIDispatchInfo.linkageReturnRegister = buildVolatileAndReturnDependencies(callNode, _JNIDispatchInfo.callPostDeps, true);705706for (int32_t i=0; i<callPost; i++)707{708TR::RegisterDependency *dep = _JNIDispatchInfo.callPostDeps->getPostConditions()->getRegisterDependency(i);709if (dep->getRealRegister() == _systemLinkage->getProperties().getIntegerScratchRegister(1))710{711_JNIDispatchInfo.dispatchTrampolineRegister = dep->getRegister();712break;713}714}715716buildJNIMergeLabelDependencies(callNode, killNonVolatileGPRs);717}718719TR::Instruction *720J9::X86::AMD64::JNILinkage::generateMethodDispatch(721TR::Node *callNode,722bool isJNIGCPoint,723uintptr_t targetAddress)724{725TR::ResolvedMethodSymbol *callSymbol = callNode->getSymbol()->castToResolvedMethodSymbol();726TR::RealRegister *espReal = machine()->getRealRegister(TR::RealRegister::esp);727TR::Register *vmThreadReg = cg()->getMethodMetaDataRegister();728intptr_t argSize = _JNIDispatchInfo.argSize;729TR::SymbolReference *methodSymRef= callNode->getSymbolReference();730TR_J9VMBase *fej9 = (TR_J9VMBase *)(comp()->fe());731732if (methodSymRef->getReferenceNumber()>=TR_AMD64numRuntimeHelpers)733{734fej9->reserveTrampolineIfNecessary(comp(), methodSymRef, false);735}736737// Load machine bp esp + offsetof(J9CInterpreterStackFrame, machineBP) + argSize738//739generateRegMemInstruction(TR::InstOpCode::LRegMem(),740callNode,741vmThreadReg,742generateX86MemoryReference(espReal, offsetof(J9CInterpreterStackFrame, machineBP) + argSize, cg()),743cg());744745// Dispatch JNI method directly.746//747TR_ASSERT(_JNIDispatchInfo.dispatchTrampolineRegister, "No trampoline scratch register available for directJNI dispatch.");748749// TODO: Need an AOT relocation here.750//751// The code below is disabled because of a problem with direct call to JNI methods752// through trampoline. The problem is that at the time of generation of the trampoline753// we don't know if we did directToJNI call or we are calling the native thunk. In case754// we are calling the thunk the code below won't work and it will give the caller address755// of the C native and we crash.756//757758759static const TR_ExternalRelocationTargetKind reloTypes[] = {TR_JNIVirtualTargetAddress, TR_NoRelocation /*Interfaces*/, TR_JNIStaticTargetAddress, TR_JNISpecialTargetAddress};760int reloType = callSymbol->getMethodKind()-1; //method kinds are 1-based!!761762TR_ASSERT(reloTypes[reloType] != TR_NoRelocation, "There shouldn't be direct JNI interface calls!");763764TR::X86RegInstruction *patchedInstr=765generateRegImm64Instruction(766TR::InstOpCode::MOV8RegImm64,767callNode,768_JNIDispatchInfo.dispatchTrampolineRegister,769targetAddress,770cg(), reloTypes[reloType]);771772TR::X86RegInstruction *instr = generateRegInstruction(773TR::InstOpCode::CALLReg,774callNode,775_JNIDispatchInfo.dispatchTrampolineRegister,776_JNIDispatchInfo.callPostDeps,777cg());778cg()->getJNICallSites().push_front(new (trHeapMemory()) TR_Pair<TR_ResolvedMethod, TR::Instruction>(callSymbol->getResolvedMethod(), patchedInstr));779780if (isJNIGCPoint)781instr->setNeedsGCMap(_systemLinkage->getProperties().getPreservedRegisterMapForGC());782783if (_JNIDispatchInfo.dispatchTrampolineRegister)784cg()->stopUsingRegister(_JNIDispatchInfo.dispatchTrampolineRegister);785786// Clean up argument area if not callee cleanup linkage.787//788// The total C stack argument size includes the push of the VMThread register plus789// the memory arguments for the call. Only clean up the memory argument portion.790//791if (!cg()->getJNILinkageCalleeCleanup())792{793intptr_t cleanUpSize = argSize - TR::Compiler->om.sizeofReferenceAddress();794795if (comp()->target().is64Bit())796TR_ASSERT(cleanUpSize <= 0x7fffffff, "Caller cleanup argument size too large for one instruction on AMD64.");797798799if (cleanUpSize != 0)800{801TR::InstOpCode::Mnemonic op = (cleanUpSize >= -128 && cleanUpSize <= 127) ? TR::InstOpCode::ADDRegImms() : TR::InstOpCode::ADDRegImm4();802generateRegImmInstruction(op, callNode, espReal, cleanUpSize, cg());803}804}805806return instr;807}808809810void J9::X86::AMD64::JNILinkage::releaseVMAccess(TR::Node *callNode)811{812// Release VM access (spin lock).813//814// mov scratch1, [rbp+publicFlags]815// loopHead:816// mov scratch2, scratch1817// test scratch1, constReleaseVMAccessOutOfLineMask818// jne longReleaseSnippet819// and scratch2, constReleaseVMAccessMask820// [l]cmpxchg [rbp+publicFlags], scratch2821// jne pauseSnippet OR loopHead822// longReleaseRestart:823// scratch1 <-> RAX824// scratch2 <-> NoReg825// scratch3 <-> NoReg826//827TR::InstOpCode::Mnemonic op;828829TR::Register *vmThreadReg = cg()->getMethodMetaDataRegister();830TR::Register *scratchReg1 = cg()->allocateRegister();831TR::Register *scratchReg2 = cg()->allocateRegister();832TR::Register *scratchReg3 = NULL;833TR_J9VMBase *fej9 = (TR_J9VMBase *)(fe());834835generateRegMemInstruction(TR::InstOpCode::LRegMem(),836callNode,837scratchReg1,838generateX86MemoryReference(vmThreadReg, fej9->thisThreadGetPublicFlagsOffset(), cg()),839cg());840841TR::LabelSymbol *loopHeadLabel = generateLabelSymbol(cg());842843// Loop head844//845generateLabelInstruction(TR::InstOpCode::label, callNode, loopHeadLabel, cg());846generateRegRegInstruction(TR::InstOpCode::MOVRegReg(), callNode, scratchReg2, scratchReg1, cg());847848TR::LabelSymbol *longReleaseSnippetLabel = generateLabelSymbol(cg());849TR::LabelSymbol *longReleaseRestartLabel = generateLabelSymbol(cg());850851uintptr_t mask = fej9->constReleaseVMAccessOutOfLineMask();852853if (comp()->target().is64Bit() && (mask > 0x7fffffff))854{855if (!scratchReg3)856scratchReg3 = cg()->allocateRegister();857858generateRegImm64Instruction(TR::InstOpCode::MOV8RegImm64, callNode, scratchReg3, mask, cg());859generateRegRegInstruction(TR::InstOpCode::TEST8RegReg, callNode, scratchReg1, scratchReg3, cg());860}861else862{863op = (mask <= 255) ? TR::InstOpCode::TEST1RegImm1 : TR::InstOpCode::TEST4RegImm4;864generateRegImmInstruction(op, callNode, scratchReg1, mask, cg());865}866generateLabelInstruction(TR::InstOpCode::JNE4, callNode, longReleaseSnippetLabel, cg());867868{869TR_OutlinedInstructionsGenerator og(longReleaseSnippetLabel, callNode, cg());870auto helper = comp()->getSymRefTab()->findOrCreateReleaseVMAccessSymbolRef(comp()->getMethodSymbol());871generateImmSymInstruction(TR::InstOpCode::CALLImm4, callNode, (uintptr_t)helper->getMethodAddress(), helper, cg());872generateLabelInstruction(TR::InstOpCode::JMP4, callNode, longReleaseRestartLabel, cg());873og.endOutlinedInstructionSequence();874}875876mask = fej9->constReleaseVMAccessMask();877878if (comp()->target().is64Bit() && (mask > 0x7fffffff))879{880if (!scratchReg3)881scratchReg3 = cg()->allocateRegister();882883generateRegImm64Instruction(TR::InstOpCode::MOV8RegImm64, callNode, scratchReg3, mask, cg());884generateRegRegInstruction(TR::InstOpCode::AND8RegReg, callNode, scratchReg2, scratchReg3, cg());885}886else887{888op = (mask <= 255) ? TR::InstOpCode::AND1RegImm1 : TR::InstOpCode::AND4RegImm4;889generateRegImmInstruction(op, callNode, scratchReg2, mask, cg());890}891892op = comp()->target().isSMP() ? TR::InstOpCode::LCMPXCHGMemReg() : TR::InstOpCode::CMPXCHGMemReg(cg());893generateMemRegInstruction(894op,895callNode,896generateX86MemoryReference(vmThreadReg, fej9->thisThreadGetPublicFlagsOffset(), cg()),897scratchReg2,898cg());899900generateLabelInstruction(TR::InstOpCode::JNE4, callNode, loopHeadLabel, cg());901902int8_t numDeps = scratchReg3 ? 3 : 2;903TR::RegisterDependencyConditions *deps = generateRegisterDependencyConditions(numDeps, numDeps, cg());904deps->addPreCondition(scratchReg1, TR::RealRegister::eax, cg());905deps->addPostCondition(scratchReg1, TR::RealRegister::eax, cg());906cg()->stopUsingRegister(scratchReg1);907908deps->addPreCondition(scratchReg2, TR::RealRegister::NoReg, cg());909deps->addPostCondition(scratchReg2, TR::RealRegister::NoReg, cg());910cg()->stopUsingRegister(scratchReg2);911912if (scratchReg3)913{914deps->addPreCondition(scratchReg3, TR::RealRegister::NoReg, cg());915deps->addPostCondition(scratchReg3, TR::RealRegister::NoReg, cg());916cg()->stopUsingRegister(scratchReg3);917}918919deps->stopAddingConditions();920921generateLabelInstruction(TR::InstOpCode::label, callNode, longReleaseRestartLabel, deps, cg());922}923924925void J9::X86::AMD64::JNILinkage::acquireVMAccess(TR::Node *callNode)926{927// Re-acquire VM access.928//929// xor scratch1, scratch1930// mov rdi, constAcquireVMAccessOutOfLineMask931// [l]cmpxchg [rbp + publicFlags], rdi932// jne longReacquireSnippetLabel933// longReacquireRestartLabel:934//935936TR::Register *vmThreadReg = cg()->getMethodMetaDataRegister();937TR::Register *scratchReg1 = cg()->allocateRegister();938TR::Register *scratchReg2 = cg()->allocateRegister();939940generateRegRegInstruction(TR::InstOpCode::XORRegReg(), callNode, scratchReg1, scratchReg1, cg());941942TR_J9VMBase *fej9 = (TR_J9VMBase *)(fe());943uintptr_t mask = fej9->constAcquireVMAccessOutOfLineMask();944945if (comp()->target().is64Bit() && (mask > 0x7fffffff))946generateRegImm64Instruction(TR::InstOpCode::MOV8RegImm64, callNode, scratchReg2, mask, cg());947else948generateRegImmInstruction(TR::InstOpCode::MOV4RegImm4, callNode, scratchReg2, mask, cg());949950TR::LabelSymbol *longReacquireSnippetLabel = generateLabelSymbol(cg());951TR::LabelSymbol *longReacquireRestartLabel = generateLabelSymbol(cg());952953TR::InstOpCode::Mnemonic op = comp()->target().isSMP() ? TR::InstOpCode::LCMPXCHGMemReg() : TR::InstOpCode::CMPXCHGMemReg(cg());954generateMemRegInstruction(955op,956callNode,957generateX86MemoryReference(vmThreadReg, fej9->thisThreadGetPublicFlagsOffset(), cg()),958scratchReg2,959cg());960generateLabelInstruction(TR::InstOpCode::JNE4, callNode, longReacquireSnippetLabel, cg());961962// TODO: ecx may hold a reference across this snippet963// If the return type is address something needs to be represented in the964// register map for the snippet.965//966{967TR_OutlinedInstructionsGenerator og(longReacquireSnippetLabel, callNode, cg());968auto helper = comp()->getSymRefTab()->findOrCreateAcquireVMAccessSymbolRef(comp()->getMethodSymbol());969generateImmSymInstruction(TR::InstOpCode::CALLImm4, callNode, (uintptr_t)helper->getMethodAddress(), helper, cg());970generateLabelInstruction(TR::InstOpCode::JMP4, callNode, longReacquireRestartLabel, cg());971og.endOutlinedInstructionSequence();972}973TR::RegisterDependencyConditions *deps = generateRegisterDependencyConditions(2, 2, cg());974deps->addPreCondition(scratchReg1, TR::RealRegister::eax, cg());975deps->addPostCondition(scratchReg1, TR::RealRegister::eax, cg());976cg()->stopUsingRegister(scratchReg1);977978deps->addPreCondition(scratchReg2, TR::RealRegister::NoReg, cg());979deps->addPostCondition(scratchReg2, TR::RealRegister::NoReg, cg());980cg()->stopUsingRegister(scratchReg2);981982deps->stopAddingConditions();983984generateLabelInstruction(TR::InstOpCode::label, callNode, longReacquireRestartLabel, deps, cg());985}986987988#ifdef J9VM_INTERP_ATOMIC_FREE_JNI989void J9::X86::AMD64::JNILinkage::releaseVMAccessAtomicFree(TR::Node *callNode)990{991TR::Register *vmThreadReg = cg()->getMethodMetaDataRegister();992993TR_J9VMBase *fej9 = (TR_J9VMBase *)(fe());994995generateMemImmInstruction(TR::InstOpCode::S8MemImm4,996callNode,997generateX86MemoryReference(vmThreadReg, offsetof(struct J9VMThread, inNative), cg()),9981,999cg());10001001#if !defined(J9VM_INTERP_ATOMIC_FREE_JNI_USES_FLUSH)1002TR::MemoryReference *mr = generateX86MemoryReference(cg()->machine()->getRealRegister(TR::RealRegister::esp), intptr_t(0), cg());1003mr->setRequiresLockPrefix();1004generateMemImmInstruction(TR::InstOpCode::OR4MemImms, callNode, mr, 0, cg());1005#endif /* !J9VM_INTERP_ATOMIC_FREE_JNI_USES_FLUSH */10061007TR::LabelSymbol *longReleaseSnippetLabel = generateLabelSymbol(cg());1008TR::LabelSymbol *longReleaseRestartLabel = generateLabelSymbol(cg());10091010static_assert(IS_32BIT_SIGNED(J9_PUBLIC_FLAGS_VM_ACCESS), "J9_PUBLIC_FLAGS_VM_ACCESS must fit in immediate");1011generateMemImmInstruction(J9_PUBLIC_FLAGS_VM_ACCESS < 128 ? TR::InstOpCode::CMP4MemImms : TR::InstOpCode::CMP4MemImm4,1012callNode,1013generateX86MemoryReference(vmThreadReg, fej9->thisThreadGetPublicFlagsOffset(), cg()),1014J9_PUBLIC_FLAGS_VM_ACCESS,1015cg());1016generateLabelInstruction(TR::InstOpCode::JNE4, callNode, longReleaseSnippetLabel, cg());1017generateLabelInstruction(TR::InstOpCode::label, callNode, longReleaseRestartLabel, cg());10181019TR_OutlinedInstructionsGenerator og(longReleaseSnippetLabel, callNode, cg());1020auto helper = comp()->getSymRefTab()->findOrCreateReleaseVMAccessSymbolRef(comp()->getMethodSymbol());1021generateImmSymInstruction(TR::InstOpCode::CALLImm4, callNode, (uintptr_t)helper->getMethodAddress(), helper, cg());1022generateLabelInstruction(TR::InstOpCode::JMP4, callNode, longReleaseRestartLabel, cg());1023og.endOutlinedInstructionSequence();1024}102510261027void J9::X86::AMD64::JNILinkage::acquireVMAccessAtomicFree(TR::Node *callNode)1028{1029TR::Register *vmThreadReg = cg()->getMethodMetaDataRegister();10301031TR_J9VMBase *fej9 = (TR_J9VMBase *)(fe());10321033generateMemImmInstruction(TR::InstOpCode::S8MemImm4,1034callNode,1035generateX86MemoryReference(vmThreadReg, offsetof(struct J9VMThread, inNative), cg()),10360,1037cg());10381039#if !defined(J9VM_INTERP_ATOMIC_FREE_JNI_USES_FLUSH)1040TR::MemoryReference *mr = generateX86MemoryReference(cg()->machine()->getRealRegister(TR::RealRegister::esp), intptr_t(0), cg());1041mr->setRequiresLockPrefix();1042generateMemImmInstruction(TR::InstOpCode::OR4MemImms, callNode, mr, 0, cg());1043#endif /* !J9VM_INTERP_ATOMIC_FREE_JNI_USES_FLUSH */10441045TR::LabelSymbol *longAcquireSnippetLabel = generateLabelSymbol(cg());1046TR::LabelSymbol *longAcquireRestartLabel = generateLabelSymbol(cg());10471048static_assert(IS_32BIT_SIGNED(J9_PUBLIC_FLAGS_VM_ACCESS), "J9_PUBLIC_FLAGS_VM_ACCESS must fit in immediate");1049generateMemImmInstruction(J9_PUBLIC_FLAGS_VM_ACCESS < 128 ? TR::InstOpCode::CMP4MemImms : TR::InstOpCode::CMP4MemImm4,1050callNode,1051generateX86MemoryReference(vmThreadReg, fej9->thisThreadGetPublicFlagsOffset(), cg()),1052J9_PUBLIC_FLAGS_VM_ACCESS,1053cg());1054generateLabelInstruction(TR::InstOpCode::JNE4, callNode, longAcquireSnippetLabel, cg());1055generateLabelInstruction(TR::InstOpCode::label, callNode, longAcquireRestartLabel, cg());10561057TR_OutlinedInstructionsGenerator og(longAcquireSnippetLabel, callNode, cg());1058auto helper = comp()->getSymRefTab()->findOrCreateAcquireVMAccessSymbolRef(comp()->getMethodSymbol());1059generateImmSymInstruction(TR::InstOpCode::CALLImm4, callNode, (uintptr_t)helper->getMethodAddress(), helper, cg());1060generateLabelInstruction(TR::InstOpCode::JMP4, callNode, longAcquireRestartLabel, cg());1061og.endOutlinedInstructionSequence();1062}106310641065#endif /* J9VM_INTERP_ATOMIC_FREE_JNI */10661067void J9::X86::AMD64::JNILinkage::cleanupReturnValue(1068TR::Node *callNode,1069TR::Register *linkageReturnReg,1070TR::Register *targetReg)1071{1072if (!callNode->getOpCode().isFloatingPoint())1073{1074// Native and JNI methods may not return a full register in some cases so we need to get the declared1075// type so that we sign and zero extend the narrower integer return types properly.1076//1077TR::InstOpCode::Mnemonic op;1078TR::SymbolReference *callSymRef = callNode->getSymbolReference();1079TR::ResolvedMethodSymbol *callSymbol = callNode->getSymbol()->castToResolvedMethodSymbol();1080TR_ResolvedMethod *resolvedMethod = callSymbol->getResolvedMethod();10811082bool isUnsigned = resolvedMethod->returnTypeIsUnsigned();10831084switch (resolvedMethod->returnType())1085{1086case TR::Int8:1087if (comp()->getSymRefTab()->isReturnTypeBool(callSymRef))1088{1089// For bool return type, must check whether value returned by1090// JNI is zero (false) or non-zero (true) to yield Java result1091generateRegRegInstruction(TR::InstOpCode::TEST1RegReg, callNode,1092linkageReturnReg, linkageReturnReg, cg());1093generateRegInstruction(TR::InstOpCode::SETNE1Reg, callNode, linkageReturnReg, cg());1094op = comp()->target().is64Bit() ? TR::InstOpCode::MOVZXReg8Reg1 : TR::InstOpCode::MOVZXReg4Reg1;1095}1096else if (isUnsigned)1097{1098op = comp()->target().is64Bit() ? TR::InstOpCode::MOVZXReg8Reg1 : TR::InstOpCode::MOVZXReg4Reg1;1099}1100else1101{1102op = comp()->target().is64Bit() ? TR::InstOpCode::MOVSXReg8Reg1 : TR::InstOpCode::MOVSXReg4Reg1;1103}1104break;1105case TR::Int16:1106if (isUnsigned)1107{1108op = comp()->target().is64Bit() ? TR::InstOpCode::MOVZXReg8Reg2 : TR::InstOpCode::MOVZXReg4Reg2;1109}1110else1111{1112op = comp()->target().is64Bit() ? TR::InstOpCode::MOVSXReg8Reg2 : TR::InstOpCode::MOVSXReg4Reg2;1113}1114break;1115default:1116// TR::Address, TR_[US]Int64, TR_[US]Int321117//1118op = (linkageReturnReg != targetReg) ? TR::InstOpCode::MOVRegReg() : TR::InstOpCode::bad;1119break;1120}11211122if (op != TR::InstOpCode::bad)1123generateRegRegInstruction(op, callNode, targetReg, linkageReturnReg, cg());1124}1125}112611271128void J9::X86::AMD64::JNILinkage::checkForJNIExceptions(TR::Node *callNode)1129{1130TR::Register *vmThreadReg = cg()->getMethodMetaDataRegister();1131TR_J9VMBase *fej9 = (TR_J9VMBase *)(fe());11321133// Check exceptions.1134//1135generateMemImmInstruction(TR::InstOpCode::CMPMemImms(), callNode, generateX86MemoryReference(vmThreadReg, fej9->thisThreadGetCurrentExceptionOffset(), cg()), 0, cg());11361137TR::LabelSymbol *snippetLabel = generateLabelSymbol(cg());1138TR::Instruction *instr = generateLabelInstruction(TR::InstOpCode::JNE4, callNode, snippetLabel, cg());11391140uint32_t gcMap = _systemLinkage->getProperties().getPreservedRegisterMapForGC();1141if (comp()->target().is32Bit())1142{1143gcMap |= (_JNIDispatchInfo.argSize<<14);1144}1145instr->setNeedsGCMap(gcMap);11461147TR::Snippet *snippet =1148new (trHeapMemory()) TR::X86CheckFailureSnippet(cg(),1149cg()->symRefTab()->findOrCreateRuntimeHelper(TR_throwCurrentException),1150snippetLabel,1151instr,1152_JNIDispatchInfo.requiresFPstackPop);1153cg()->addSnippet(snippet);1154}115511561157void J9::X86::AMD64::JNILinkage::cleanupJNIRefPool(TR::Node *callNode)1158{1159// Must check to see if the ref pool was used and clean them up if so--or we1160// leave a bunch of pinned garbage behind that screws up the gc quality forever.1161//1162TR_J9VMBase *fej9 = (TR_J9VMBase *)(fe());1163static_assert(IS_32BIT_SIGNED(J9_SSF_JIT_JNI_FRAME_COLLAPSE_BITS), "J9_SSF_JIT_JNI_FRAME_COLLAPSE_BITS must fit in immediate");11641165TR::RealRegister *espReal = machine()->getRealRegister(TR::RealRegister::esp);11661167TR::LabelSymbol *refPoolSnippetLabel = generateLabelSymbol(cg());1168TR::LabelSymbol *refPoolRestartLabel = generateLabelSymbol(cg());11691170generateMemImmInstruction(J9_SSF_JIT_JNI_FRAME_COLLAPSE_BITS <= 255 ? TR::InstOpCode::TEST1MemImm1 : TR::InstOpCode::TESTMemImm4(),1171callNode,1172generateX86MemoryReference(espReal, fej9->constJNICallOutFrameFlagsOffset(), cg()),1173J9_SSF_JIT_JNI_FRAME_COLLAPSE_BITS,1174cg());11751176generateLabelInstruction(TR::InstOpCode::JNE4, callNode, refPoolSnippetLabel, cg());1177generateLabelInstruction(TR::InstOpCode::label, callNode, refPoolRestartLabel, cg());11781179TR_OutlinedInstructionsGenerator og(refPoolSnippetLabel, callNode, cg());1180generateHelperCallInstruction(callNode, TR_AMD64jitCollapseJNIReferenceFrame, NULL, cg());1181generateLabelInstruction(TR::InstOpCode::JMP4, callNode, refPoolRestartLabel, cg());1182og.endOutlinedInstructionSequence();1183}118411851186TR::Register *J9::X86::AMD64::JNILinkage::buildDirectDispatch(1187TR::Node *callNode,1188bool spillFPRegs)1189{1190TR::SymbolReference *callSymRef = callNode->getSymbolReference();1191TR::MethodSymbol *callSymbol = callSymRef->getSymbol()->castToMethodSymbol();11921193bool isGPUHelper = callSymbol->isHelper() && (callSymRef->getReferenceNumber() == TR_estimateGPU ||1194callSymRef->getReferenceNumber() == TR_getStateGPU ||1195callSymRef->getReferenceNumber() == TR_regionEntryGPU ||1196callSymRef->getReferenceNumber() == TR_allocateGPUKernelParms ||1197callSymRef->getReferenceNumber() == TR_copyToGPU ||1198callSymRef->getReferenceNumber() == TR_launchGPUKernel ||1199callSymRef->getReferenceNumber() == TR_copyFromGPU ||1200callSymRef->getReferenceNumber() == TR_invalidateGPU ||1201callSymRef->getReferenceNumber() == TR_flushGPU ||1202callSymRef->getReferenceNumber() == TR_regionExitGPU);1203if (callSymbol->isJNI() || isGPUHelper)1204{1205return buildDirectJNIDispatch(callNode);1206}12071208TR_ASSERT(false, "call through TR::AMD64SystemLinkage::buildDirectDispatch instead.\n");1209return NULL;1210}121112121213void1214J9::X86::AMD64::JNILinkage::populateJNIDispatchInfo()1215{1216_JNIDispatchInfo.numJNIFrameSlotsPushed = 5;12171218_JNIDispatchInfo.JNIReturnRegister = NULL;1219_JNIDispatchInfo.linkageReturnRegister = NULL;12201221_JNIDispatchInfo.callPreDeps = NULL;1222_JNIDispatchInfo.callPostDeps = NULL;1223_JNIDispatchInfo.mergeLabelPostDeps = NULL;1224_JNIDispatchInfo.dispatchTrampolineRegister = NULL;12251226_JNIDispatchInfo.argSize = 0;12271228_JNIDispatchInfo.requiresFPstackPop = false;12291230_JNIDispatchInfo.dedicatedFrameRegisterIndex = _systemLinkage->getProperties().getIntegerScratchRegister(0);1231}123212331234TR::Register *J9::X86::AMD64::JNILinkage::buildDirectJNIDispatch(TR::Node *callNode)1235{1236#ifdef DEBUG1237if (debug("reportJNI"))1238{1239printf("AMD64 JNI Dispatch: %s calling %s\n", comp()->signature(), comp()->getDebug()->getName(callNode->getSymbolReference()));1240}1241#endif1242TR::SymbolReference *callSymRef = callNode->getSymbolReference();1243TR::MethodSymbol *callSymbol = callSymRef->getSymbol()->castToMethodSymbol();12441245bool isGPUHelper = callSymbol->isHelper() && (callSymRef->getReferenceNumber() == TR_estimateGPU ||1246callSymRef->getReferenceNumber() == TR_getStateGPU ||1247callSymRef->getReferenceNumber() == TR_regionEntryGPU ||1248callSymRef->getReferenceNumber() == TR_allocateGPUKernelParms ||1249callSymRef->getReferenceNumber() == TR_copyToGPU ||1250callSymRef->getReferenceNumber() == TR_launchGPUKernel ||1251callSymRef->getReferenceNumber() == TR_copyFromGPU ||1252callSymRef->getReferenceNumber() == TR_invalidateGPU ||1253callSymRef->getReferenceNumber() == TR_flushGPU ||1254callSymRef->getReferenceNumber() == TR_regionExitGPU);12551256static bool keepVMDuringGPUHelper = feGetEnv("TR_KeepVMDuringGPUHelper") ? true : false;12571258TR::Register *vmThreadReg = cg()->getMethodMetaDataRegister();1259TR::RealRegister *espReal = machine()->getRealRegister(TR::RealRegister::esp);12601261TR::ResolvedMethodSymbol *resolvedMethodSymbol;1262TR_ResolvedMethod *resolvedMethod;1263TR::SymbolReference *gpuHelperSymRef;1264TR_J9VMBase *fej9 = (TR_J9VMBase *)(fe());12651266bool dropVMAccess;1267bool isJNIGCPoint;1268bool killNonVolatileGPRs;1269bool checkExceptions;1270bool createJNIFrame;1271bool tearDownJNIFrame;1272bool wrapRefs;1273bool passReceiver;1274bool passThread;12751276if (!isGPUHelper)1277{1278resolvedMethodSymbol = callNode->getSymbol()->castToResolvedMethodSymbol();1279resolvedMethod = resolvedMethodSymbol->getResolvedMethod();1280dropVMAccess = !fej9->jniRetainVMAccess(resolvedMethod);1281isJNIGCPoint = !fej9->jniNoGCPoint(resolvedMethod);1282killNonVolatileGPRs = isJNIGCPoint;1283checkExceptions = !fej9->jniNoExceptionsThrown(resolvedMethod);1284createJNIFrame = !fej9->jniNoNativeMethodFrame(resolvedMethod);1285tearDownJNIFrame = !fej9->jniNoSpecialTeardown(resolvedMethod);1286wrapRefs = !fej9->jniDoNotWrapObjects(resolvedMethod);1287passReceiver = !fej9->jniDoNotPassReceiver(resolvedMethod);1288passThread = !fej9->jniDoNotPassThread(resolvedMethod);1289}1290else1291{1292gpuHelperSymRef = comp()->getSymRefTab()->methodSymRefFromName(comp()->getMethodSymbol(), "com/ibm/jit/JITHelpers", "GPUHelper", "()V", TR::MethodSymbol::Static);1293resolvedMethodSymbol = gpuHelperSymRef->getSymbol()->castToResolvedMethodSymbol();1294resolvedMethod = resolvedMethodSymbol->getResolvedMethod();12951296if (keepVMDuringGPUHelper || (callSymRef->getReferenceNumber() == TR_copyToGPU || callSymRef->getReferenceNumber() == TR_copyFromGPU || callSymRef->getReferenceNumber() == TR_flushGPU || callSymRef->getReferenceNumber() == TR_regionExitGPU || callSymRef->getReferenceNumber() == TR_estimateGPU))1297dropVMAccess = false; //TR_copyToGPU, TR_copyFromGPU, TR_regionExitGPU (and all others if keepVMDuringGPUHelper is true)1298else1299dropVMAccess = true; //TR_regionEntryGPU, TR_launchGPUKernel, TR_estimateGPU, TR_allocateGPUKernelParms, ... (only if keepVMDuringGPUHelper is false)13001301isJNIGCPoint = true;1302killNonVolatileGPRs = isJNIGCPoint;1303checkExceptions = false;1304createJNIFrame = true;1305tearDownJNIFrame = true;1306wrapRefs = false; //unused for this code path1307passReceiver = true;1308passThread = false;1309}13101311populateJNIDispatchInfo();13121313static char * disablePureFn = feGetEnv("TR_DISABLE_PURE_FUNC_RECOGNITION");1314if (!isGPUHelper)1315{1316if (resolvedMethodSymbol->canDirectNativeCall())1317{1318dropVMAccess = false;1319killNonVolatileGPRs = false;1320isJNIGCPoint = false;1321checkExceptions = false;1322createJNIFrame = false;1323tearDownJNIFrame = false;1324}1325else if (callNode->getSymbol()->castToResolvedMethodSymbol()->isPureFunction() && (disablePureFn == NULL))1326{1327dropVMAccess = false;1328isJNIGCPoint = false;1329checkExceptions = false;1330}1331}13321333// Anchor the Java frame here to establish the top of the frame. The reason this must occur here1334// is because the subsequent manual adjustments of the stack pointer confuse the vfp logic.1335// This should be fixed in a subsquent revision of that code.1336//1337TR::X86VFPDedicateInstruction *vfpDedicateInstruction =1338generateVFPDedicateInstruction(machine()->getRealRegister(_JNIDispatchInfo.dedicatedFrameRegisterIndex), callNode, cg());13391340// First, build a JNI callout frame on the Java stack.1341//1342TR::LabelSymbol *returnAddrLabel = generateLabelSymbol(cg());1343if (createJNIFrame)1344{1345if (isGPUHelper)1346callNode->setSymbolReference(gpuHelperSymRef);13471348buildJNICallOutFrame(callNode, returnAddrLabel);13491350if (isGPUHelper)1351callNode->setSymbolReference(callSymRef); //change back to callSymRef afterwards1352}13531354// Switch from the Java stack to the C stack:1355TR::J9LinkageUtils::switchToMachineCStack(callNode, cg());13561357// Preserve the VMThread pointer on the C stack.1358// Adjust the argSize to include the just pushed VMThread pointer.1359//1360generateRegInstruction(TR::InstOpCode::PUSHReg, callNode, vmThreadReg, cg());1361if (passThread || isGPUHelper)1362{1363_JNIDispatchInfo.argSize = TR::Compiler->om.sizeofReferenceAddress();1364}13651366TR::LabelSymbol *startJNISequence = generateLabelSymbol(cg());1367startJNISequence->setStartInternalControlFlow();1368generateLabelInstruction(TR::InstOpCode::label, callNode, startJNISequence, cg());13691370if (isGPUHelper)1371callNode->setSymbolReference(gpuHelperSymRef);13721373buildOutgoingJNIArgsAndDependencies(callNode, passThread, passReceiver, killNonVolatileGPRs);13741375if (isGPUHelper)1376callNode->setSymbolReference(callSymRef); //change back to callSymRef afterwards13771378if (dropVMAccess)1379{1380#ifdef J9VM_INTERP_ATOMIC_FREE_JNI1381releaseVMAccessAtomicFree(callNode);1382#else1383releaseVMAccess(callNode);1384#endif1385}13861387uintptr_t targetAddress;13881389if (isGPUHelper)1390{1391callNode->setSymbolReference(gpuHelperSymRef);1392targetAddress = (uintptr_t)callSymbol->getMethodAddress();1393}1394else1395{1396TR::ResolvedMethodSymbol *callSymbol1 = callNode->getSymbol()->castToResolvedMethodSymbol();1397targetAddress = (uintptr_t)callSymbol1->getResolvedMethod()->startAddressForJNIMethod(comp());1398}13991400TR::Instruction *callInstr = generateMethodDispatch(callNode, isJNIGCPoint, targetAddress);14011402if (isGPUHelper)1403callNode->setSymbolReference(callSymRef); //change back to callSymRef afterwards14041405// TODO: will need an AOT relocation for this one at some point.1406// Lay down a label for the frame push to reference.1407//1408generateLabelInstruction(callInstr, TR::InstOpCode::label, returnAddrLabel, cg());14091410if (_JNIDispatchInfo.JNIReturnRegister)1411{1412if (isGPUHelper)1413callNode->setSymbolReference(gpuHelperSymRef);14141415cleanupReturnValue(callNode, _JNIDispatchInfo.linkageReturnRegister, _JNIDispatchInfo.JNIReturnRegister);14161417if (isGPUHelper)1418callNode->setSymbolReference(callSymRef); //change back to callSymRef afterwards14191420if (_JNIDispatchInfo.linkageReturnRegister != _JNIDispatchInfo.JNIReturnRegister)1421cg()->stopUsingRegister(_JNIDispatchInfo.linkageReturnRegister);1422}14231424// Restore the VMThread back from the C stack.1425//1426generateRegInstruction(TR::InstOpCode::POPReg, callNode, vmThreadReg, cg());14271428if (dropVMAccess)1429{1430#ifdef J9VM_INTERP_ATOMIC_FREE_JNI1431acquireVMAccessAtomicFree(callNode);1432#else1433acquireVMAccess(callNode);1434#endif1435}14361437if (resolvedMethod->returnType() == TR::Address && wrapRefs)1438{1439// Unless NULL, need to indirect once to get the real Java reference.1440//1441// This MUST be done AFTER VM access has been re-acquired!1442//1443TR::Register *targetReg = _JNIDispatchInfo.JNIReturnRegister;1444TR::LabelSymbol *nullLabel = generateLabelSymbol(cg());1445generateRegRegInstruction(TR::InstOpCode::TESTRegReg(), callNode, targetReg, targetReg, cg());1446generateLabelInstruction(TR::InstOpCode::JE4, callNode, nullLabel, cg());14471448generateRegMemInstruction(1449TR::InstOpCode::LRegMem(),1450callNode,1451targetReg,1452generateX86MemoryReference(targetReg, 0, cg()),1453cg());14541455generateLabelInstruction(TR::InstOpCode::label, callNode, nullLabel, cg());1456}14571458// 1) Store out the machine sp into the vm thread. It has to be done as sometimes1459// it gets tromped on by call backs.1460generateMemRegInstruction(1461TR::InstOpCode::SMemReg(),1462callNode,1463generateX86MemoryReference(vmThreadReg, fej9->thisThreadGetMachineSPOffset(), cg()),1464espReal,1465cg());14661467TR::J9LinkageUtils::switchToJavaStack(callNode, cg());14681469if (createJNIFrame)1470{1471generateRegMemInstruction(1472TR::InstOpCode::ADDRegMem(),1473callNode,1474espReal,1475generateX86MemoryReference(vmThreadReg, fej9->thisThreadGetJavaLiteralsOffset(), cg()),1476cg());1477}14781479if (createJNIFrame && tearDownJNIFrame)1480{1481cleanupJNIRefPool(callNode);1482}14831484// Clean up JNI callout frame.1485//1486if (createJNIFrame)1487{1488TR::X86RegImmInstruction *instr = generateRegImmInstruction(1489TR::InstOpCode::ADDRegImms(),1490callNode,1491espReal,1492_JNIDispatchInfo.numJNIFrameSlotsPushed * TR::Compiler->om.sizeofReferenceAddress(),1493cg());1494}14951496if (checkExceptions)1497{1498checkForJNIExceptions(callNode);1499}15001501generateVFPReleaseInstruction(vfpDedicateInstruction, callNode, cg());15021503TR::LabelSymbol *restartLabel = generateLabelSymbol(cg());1504restartLabel->setEndInternalControlFlow();1505generateLabelInstruction(TR::InstOpCode::label, callNode, restartLabel, _JNIDispatchInfo.mergeLabelPostDeps, cg());15061507return _JNIDispatchInfo.JNIReturnRegister;1508}150915101511#endif151215131514