Path: blob/master/runtime/compiler/z/codegen/S390CHelperLinkage.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#include "codegen/S390CHelperLinkage.hpp"23#include "codegen/S390PrivateLinkage.hpp"2425#include "codegen/CodeGenerator.hpp"26#include "codegen/GCStackAtlas.hpp"27#include "codegen/Linkage_inlines.hpp"28#include "codegen/Snippet.hpp"29#include "compile/ResolvedMethod.hpp"30#include "compile/VirtualGuard.hpp"31#include "env/CHTable.hpp"32#include "env/CompilerEnv.hpp"33#include "env/J2IThunk.hpp"34#include "env/PersistentCHTable.hpp"35#include "env/StackMemoryRegion.hpp"36#include "env/VMJ9.h"37#include "env/jittypes.h"38#include "env/j9method.h"39#include "il/Node.hpp"40#include "il/Node_inlines.hpp"41#include "il/TreeTop.hpp"42#include "il/TreeTop_inlines.hpp"43#include "infra/InterferenceGraph.hpp"44#include "z/codegen/OpMemToMem.hpp"45#include "z/codegen/S390Evaluator.hpp"46#include "z/codegen/S390GenerateInstructions.hpp"47#include "z/codegen/S390HelperCallSnippet.hpp"48#include "z/codegen/S390J9CallSnippet.hpp"49#include "z/codegen/S390StackCheckFailureSnippet.hpp"50#include "z/codegen/SystemLinkage.hpp"51#include "runtime/J9Profiler.hpp"52#include "runtime/J9ValueProfiler.hpp"5354////////////////////////////////////////////////////////////////////////////////55// J9::Z::CHelperLinkage for J956////////////////////////////////////////////////////////////////////////////////57J9::Z::CHelperLinkage::CHelperLinkage(TR::CodeGenerator * codeGen,TR_LinkageConventions elc)58: TR::Linkage(codeGen,elc)59{60TR::Compilation *comp = codeGen->comp();6162// Common Linux on Z and z/OS linkage settings63setRegisterFlag(TR::RealRegister::GPR8, Preserved);64setRegisterFlag(TR::RealRegister::GPR9, Preserved);65setRegisterFlag(TR::RealRegister::GPR10, Preserved);66setRegisterFlag(TR::RealRegister::GPR11, Preserved);67setRegisterFlag(TR::RealRegister::GPR13, Preserved);68setRegisterFlag(TR::RealRegister::GPR15, Preserved);6970#if defined(ENABLE_PRESERVED_FPRS)71// In case of 32bit Linux on Z, System Linkage only preserves FPR4 and FPR6. For all other targets, FPR8-FPR15 is72// preserved.73if (comp->target().isLinux() && comp->target().is32Bit())74{75setRegisterFlag(TR::RealRegister::FPR4, Preserved);76setRegisterFlag(TR::RealRegister::FPR6, Preserved);77}78else79{80setRegisterFlag(TR::RealRegister::FPR8, Preserved);81setRegisterFlag(TR::RealRegister::FPR9, Preserved);82setRegisterFlag(TR::RealRegister::FPR10, Preserved);83setRegisterFlag(TR::RealRegister::FPR11, Preserved);84setRegisterFlag(TR::RealRegister::FPR12, Preserved);85setRegisterFlag(TR::RealRegister::FPR13, Preserved);86setRegisterFlag(TR::RealRegister::FPR14, Preserved);87setRegisterFlag(TR::RealRegister::FPR15, Preserved);88}89#endif9091// Platform specific linkage settings92if (comp->target().isLinux())93{94setRegisterFlag(TR::RealRegister::GPR6, Preserved);95setRegisterFlag(TR::RealRegister::GPR7, Preserved);96setRegisterFlag(TR::RealRegister::GPR12, Preserved);9798setReturnAddressRegister(TR::RealRegister::GPR14);99setIntegerReturnRegister(TR::RealRegister::GPR2);100setLongReturnRegister(TR::RealRegister::GPR2);101setIntegerArgumentRegister(0, TR::RealRegister::GPR2);102setIntegerArgumentRegister(1, TR::RealRegister::GPR3);103setIntegerArgumentRegister(2, TR::RealRegister::GPR4);104setIntegerArgumentRegister(3, TR::RealRegister::GPR5);105setIntegerArgumentRegister(4, TR::RealRegister::GPR6);106107// Hardcoding register map for GC can lead to subtle bugs if linkage is changed and we did not change register map for GC.108// TODO We should write a function that goes through the list of preserved registers and prepare register map for GC.109setPreservedRegisterMapForGC(0x0000BFC0);110111// GPR5 needs to be stored before using it as argument register.112// Right now setting number of integer argument registers to 3.113// TODO When we add support for more than 3 arguments, we should change this number to 5.114setNumIntegerArgumentRegisters(3);115}116117if (comp->target().isZOS())118{119setRegisterFlag(TR::RealRegister::GPR14, Preserved);120121if (comp->target().is64Bit())122{123setRegisterFlag(TR::RealRegister::GPR12, Preserved);124125setPreservedRegisterMapForGC(0x0000FF00);126}127else128{129// 31-Bit zOS will need GPR12 for CAA register so it won't be preserved. For all other variant it is preserved130setPreservedRegisterMapForGC(0x0000EF00);131}132133if (codeGen->getSupportsVectorRegisters())134{135setRegisterFlag(TR::RealRegister::VRF16, Preserved);136setRegisterFlag(TR::RealRegister::VRF17, Preserved);137setRegisterFlag(TR::RealRegister::VRF18, Preserved);138setRegisterFlag(TR::RealRegister::VRF19, Preserved);139setRegisterFlag(TR::RealRegister::VRF20, Preserved);140setRegisterFlag(TR::RealRegister::VRF21, Preserved);141setRegisterFlag(TR::RealRegister::VRF22, Preserved);142setRegisterFlag(TR::RealRegister::VRF23, Preserved);143}144145setReturnAddressRegister(TR::RealRegister::GPR7);146setIntegerReturnRegister(TR::RealRegister::GPR3);147setLongReturnRegister(TR::RealRegister::GPR3);148setIntegerArgumentRegister(0, TR::RealRegister::GPR1);149setIntegerArgumentRegister(1, TR::RealRegister::GPR2);150setIntegerArgumentRegister(2, TR::RealRegister::GPR3);151setNumIntegerArgumentRegisters(3);152153#if defined(J9ZOS390)154setDSAPointerRegister(TR::RealRegister::GPR4);155#if defined(TR_HOST_32BIT)156setCAAPointerRegister(TR::RealRegister::GPR12);157#endif158#endif159}160161setLongLowReturnRegister(TR::RealRegister::GPR3);162setLongHighReturnRegister(TR::RealRegister::GPR2);163setFloatReturnRegister(TR::RealRegister::FPR0);164setDoubleReturnRegister(TR::RealRegister::FPR0);165setLongDoubleReturnRegister0(TR::RealRegister::FPR0);166setLongDoubleReturnRegister2(TR::RealRegister::FPR2);167setLongDoubleReturnRegister4(TR::RealRegister::FPR4);168setLongDoubleReturnRegister6(TR::RealRegister::FPR6);169setStackPointerRegister(TR::RealRegister::GPR5);170setMethodMetaDataRegister(TR::RealRegister::GPR13);171}172173174// Utility to Manage RealRegisters for Helper Call Using C Linkage175/** \brief Utility to Manage RealRegisters for Helper Call Using C Linkage.176* \details177* It is used to allocate a virtual register and assign them to the requested real register.178*/179class RealRegisterManager180{181public:182RealRegisterManager(TR::CodeGenerator *cg) :183_cg(cg),184_numberOfRegistersInUse(0)185{186memset(_Registers, 0x0, sizeof(_Registers));187}188189~RealRegisterManager()190{191for(uint8_t i = (uint8_t)TR::RealRegister::NoReg; i < (uint8_t)TR::RealRegister::NumRegisters; i++)192{193if (_Registers[i] != NULL)194{195_cg->stopUsingRegister(_Registers[i]);196}197}198}199TR::Register* use(TR::RealRegister::RegNum RealRegister)200{201if (_Registers[RealRegister] == NULL)202{203if (RealRegister >= TR::RealRegister::FirstFPR && RealRegister <= TR::RealRegister::LastFPR)204_Registers[RealRegister] = _cg->allocateRegister(TR_FPR);205else if (RealRegister >= TR::RealRegister::FirstVRF && RealRegister <= TR::RealRegister::LastVRF)206_Registers[RealRegister] = _cg->allocateRegister(TR_VRF);207else208_Registers[RealRegister] = _cg->allocateRegister();209_numberOfRegistersInUse++;210}211return _Registers[RealRegister];212}213// For the post dependency conditions we need return address register to be assigned to proper virtual register instead of the dummy virtual register214TR::RegisterDependencyConditions* buildRegisterDependencyConditions(TR::RealRegister::RegNum returnAddressReg)215{216uint32_t numDeps = numberOfRegistersInUse() - 1 + (_cg->comp()->target().is32Bit() ? 1 : 0);217218// Helper function to generate RegisterDependencyCondition adds one more to post deps for return address.219// Hence asking one less post deps so this won't create a problem when combined with other dependency condition for ICF.220TR::RegisterDependencyConditions* deps = generateRegisterDependencyConditions(0, numDeps, _cg);221for (uint8_t i = (uint8_t)TR::RealRegister::NoReg; i < (uint8_t)TR::RealRegister::NumRegisters; i++)222{223if (_Registers[i] != NULL)224{225if (i != returnAddressReg)226_Registers[i]->setPlaceholderReg();227deps->addPostCondition(_Registers[i], (TR::RealRegister::RegNum)i, DefinesDependentRegister);228}229}230231// spill all high regs232//233if (_cg->comp()->target().is32Bit())234{235TR::Register *reg = _cg->allocateRegister();236deps->addPostCondition(reg, TR::RealRegister::KillVolHighRegs);237_cg->stopUsingRegister(reg);238}239return deps;240}241242inline uint8_t numberOfRegistersInUse() const243{244return _numberOfRegistersInUse;245}246private:247uint8_t _numberOfRegistersInUse;248TR::Register* _Registers[TR::RealRegister::NumRegisters];249TR::CodeGenerator* _cg;250};251252/** \brief Build a JIT helper call.253* \details254* It generates sequence that prepares parameters for the JIT helper function and generate a helper call.255* \param callNode The node for which you are generating a helper call256* \param deps The pre register dependency conditions that will be filled by this function to attach within ICF257* \param returnReg TR::Register* allocated by consumer of this API to hold the result of the helper call,258* If passed, this function uses it to store return value from helper instead of allocating new register259* \return TR::Register *helperReturnResult, gets the return value of helper function and return to the evaluator.260*/261TR::Register * J9::Z::CHelperLinkage::buildDirectDispatch(TR::Node * callNode, TR::RegisterDependencyConditions **deps, TR::Register *returnReg)262{263RealRegisterManager RealRegisters(cg());264bool isHelperCallWithinICF = deps != NULL;265// TODO: Currently only jitInstanceOf is fast path helper. Need to modify following condition if we add support for other fast path only helpers266bool isFastPathOnly = callNode->getOpCodeValue() == TR::instanceof;267traceMsg(comp(),"%s: Internal Control Flow in OOL : %s\n",callNode->getOpCode().getName(),isHelperCallWithinICF ? "true" : "false" );268for (int i = TR::RealRegister::FirstGPR; i < TR::RealRegister::NumRegisters; i++)269{270if (!self()->getPreserved(REGNUM(i)) && cg()->machine()->getRealRegister(i)->getState() != TR::RealRegister::Locked)271{272RealRegisters.use((TR::RealRegister::RegNum)i);273}274}275TR::RegisterDependencyConditions *childNodeRegDeps = generateRegisterDependencyConditions(0,callNode->getNumChildren(), cg());276TR::RegisterDependencyConditions* preDeps = generateRegisterDependencyConditions(callNode->getNumChildren()+1, 0, cg());277TR::Register *vmThreadRegister = cg()->getMethodMetaDataRealRegister();278TR::Register *vmThreadArgReg = cg()->allocateRegister();279generateRRInstruction(cg(), TR::InstOpCode::getLoadRegOpCode(), callNode, vmThreadArgReg, vmThreadRegister);280preDeps->addPreCondition(vmThreadArgReg, getIntegerArgumentRegister(0));281// TODO For Time Being, it is expected that param number won't increase beyond 3 need to fix this when support for stack is there282for (int i=0; i< callNode->getNumChildren(); i++)283{284if (i < self()->getNumIntegerArgumentRegisters()-1)285preDeps->addPreCondition(cg()->gprClobberEvaluate(callNode->getChild(i)), getIntegerArgumentRegister(i+1));286else287TR_ASSERT(false,"Parameters on Stack not supported yet");288childNodeRegDeps->addPostConditionIfNotAlreadyInserted(callNode->getChild(i)->getRegister(), TR::RealRegister::AssignAny);289}290291TR::Register *javaStackPointerRegister = NULL;292/* On zOS, we need to use GPR7 as return address for fastPath helper calls293* Following line will use the System linkage's return address register for fast path294* And for regular dual mode helper, private linkage's return address register295*/296TR::RealRegister::RegNum regRANum = isFastPathOnly ? self()->getReturnAddressRegister() : cg()->getReturnAddressRegister();297TR::Register *regRA = RealRegisters.use(regRANum);298#if defined(J9ZOS390)299TR::Register *DSAPointerReg = NULL;300uint32_t offsetOfSSP = static_cast<uint32_t>(offsetof(J9VMThread, systemStackPointer));301uint32_t pointerSize = comp()->target().is64Bit() ? 7 : 3;302#endif303304TR::RegisterDependencyConditions * postDeps = RealRegisters.buildRegisterDependencyConditions(regRANum);305// If buildDirectDispatch is called within ICF we need to pass the dependencies which will be used there, else we need single dependencylist to be attached to the BRASL306// We return postdependency conditions back to evaluator to merge with ICF condition and attach to merge label307if (isHelperCallWithinICF )308*deps = new (cg()->trHeapMemory()) TR::RegisterDependencyConditions(postDeps, childNodeRegDeps, cg());309310int padding = 0;311uint32_t offsetJ9SP = static_cast<uint32_t>(offsetof(J9VMThread, sp));312TR::Instruction *cursor = NULL;313314/* Following routine will generate assembly routine as following315* #if (fastPathHelper)316* STG R5, @(vmThread+offsetOf(J9SP))317* #define zOS318* LG DSA, @(vmThread,offsetOfSSP)319* XC offsetOFSSP(vmThread, pointerSize), offsetOfSSP(vmThread)320* #define 32Bit321* LG CAA, @(DSA+CAAOffset)322* #endif323* BRASL R14,JIThelper324* #if fastPathHelper325* #define zOS326* NOP // Padding for zOS return from helper327* STG DSA, @(vmThread, offsetOfSSP)328* LG R5, @(vmThread, offsetOfJ9SP)329* #endif330*/331332if (isFastPathOnly)333{334// Storing Java Stack Pointer335javaStackPointerRegister = cg()->getStackPointerRealRegister();336cursor = generateRXInstruction(cg(), TR::InstOpCode::getStoreOpCode(), callNode, javaStackPointerRegister, generateS390MemoryReference(vmThreadRegister, offsetJ9SP, cg()));337#if defined(J9ZOS390)338padding += 2;339// Loading DSAPointer Register340DSAPointerReg = RealRegisters.use(getDSAPointerRegister());341generateRXInstruction(cg(), TR::InstOpCode::getLoadOpCode(), callNode, DSAPointerReg, generateS390MemoryReference(vmThreadRegister, offsetOfSSP, cg()));342generateSS1Instruction(cg(), TR::InstOpCode::XC, callNode, pointerSize,343generateS390MemoryReference(vmThreadRegister, offsetOfSSP, cg()),344generateS390MemoryReference(vmThreadRegister, offsetOfSSP, cg()));345#if defined(TR_HOST_32BIT)346padding += 2;347// Loading CAA Pointer Register348TR::Register *CAAPointerReg = RealRegisters.use(getCAAPointerRegister());349TR_J9VMBase *fej9 = (TR_J9VMBase *) comp()->fe();350int32_t J9TR_CAA_save_offset = fej9->getCAASaveOffset();351generateRXInstruction(cg(), TR::InstOpCode::getLoadOpCode(), callNode, CAAPointerReg, generateS390MemoryReference(DSAPointerReg, J9TR_CAA_save_offset, cg()));352#endif353#endif354}355TR::SymbolReference * callSymRef = callNode->getSymbolReference();356void * destAddr = callNode->getSymbolReference()->getSymbol()->castToMethodSymbol()->getMethodAddress();357cursor = new (cg()->trHeapMemory()) TR::S390RILInstruction(TR::InstOpCode::BRASL, callNode, regRA, destAddr, callSymRef, cg());358cursor->setDependencyConditions(preDeps);359if (isFastPathOnly)360{361#if defined(J9ZOS390)362/**363* Same as SystemLinkage call builder class, a NOP padding is needed because returning from XPLINK functions364* skips the XPLink eyecatcher and always return to a point that's 2 or 4 bytes after the return address.365*366* In 64 bit XPLINK, the caller returns with a 'branch relative on condition' instruction with a 2 byte offset:367*368* 0x47F07002 B 2(,r7)369*370* In 31-bit XPLINK, this offset is 4-byte.371*372* As a result of this, JIT'ed code that does XPLINK calls needs 2 or 4-byte NOP paddings to ensure entry to valid instruction.373*374* The BASR and NOP padding must stick together and can't have reverse spills in the middle.375* Hence, splitting the dependencies to avoid spill instructions.376*/377378cursor = new (cg()->trHeapMemory()) TR::S390NOPInstruction(TR::InstOpCode::NOP, padding, callNode, cursor, cg());379// Storing DSA Register back380cursor = generateRXInstruction(cg(), TR::InstOpCode::getStoreOpCode(), callNode, DSAPointerReg, generateS390MemoryReference(vmThreadRegister, offsetOfSSP, cg()), cursor);381#endif382// Reloading Java Stack pointer383cursor = generateRXInstruction(cg(), TR::InstOpCode::getLoadOpCode(), callNode, javaStackPointerRegister, generateS390MemoryReference(vmThreadRegister, offsetJ9SP, cg()), cursor);384}385else386{387// Fastpath helper do not expects GC call in-between so only attaching them for normal dual mode helpers388// As GC map is attached to instruction after RA is done, it is guaranteed that all the non-preserved register by system linkage are either stored in preserved register389// Or spilled to stack. We only need to mark preserved register in GC map. Only possibility of non-preserved register containing a live object is in argument to helper which should be a clobberable copy of actual object.390cursor->setNeedsGCMap(getPreservedRegisterMapForGC());391}392393// If helper call is fast path helper call and is not within ICF,394// We need to attach post dependency to the restoring of java stack pointer.395// This will assure that reverse spilling occurs after restoring of java stack pointer396if (!isHelperCallWithinICF)397cursor->setDependencyConditions(postDeps);398preDeps->stopUsingDepRegs(cg());399// Logic To Handle Return Register400TR::DataType returnType = callNode->getDataType();401if (returnReg == NULL)402{403switch(returnType)404{405case TR::NoType:406traceMsg(comp(), "ReturnType = %s\n",returnType.toString());407break;408case TR::Address:409traceMsg(comp(), "ReturnType = %s\n",returnType.toString());410returnReg = cg()->allocateCollectedReferenceRegister();411break;412case TR::Int8:413case TR::Int16:414case TR::Int32:415#ifdef TR_TARGET_64BIT416case TR::Int64:417#endif418traceMsg(comp(), "ReturnType = %s\n",returnType.toString());419returnReg = cg()->allocateRegister();420break;421default:422TR_ASSERT(false, "Unsupported Call return data type: %s\n", returnType.toString());423break;424}425}426// We need to fill returnReg only if it requested by evaluator or node returns value or address427if (returnReg != NULL)428generateRRInstruction(cg(), TR::InstOpCode::getLoadRegOpCode(), callNode, returnReg, RealRegisters.use(isFastPathOnly ? getIntegerReturnRegister():getLongHighReturnRegister()), cursor);429430return returnReg;431}432433434