Path: blob/master/runtime/compiler/optimizer/BoolArrayStoreTransformer.cpp
6000 views
/*******************************************************************************1* Copyright (c) 2018, 2020 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 J9ZTPF23#define __TPF_DO_NOT_MAP_ATOE_REMOVE24#endif2526#include "optimizer/BoolArrayStoreTransformer.hpp"27#include "compiler/il/OMRTreeTop_inlines.hpp"28#include "il/AutomaticSymbol.hpp"29#include "il/Block.hpp"30#include "il/Node.hpp"31#include "il/Node_inlines.hpp"32#include "il/TreeTop.hpp"33#include "infra/Cfg.hpp"34#include "infra/ILWalk.hpp"35#include <deque>36#include <stack>3738/*39* This transformer is used to make sure the behavior of bastore in JIT code40* complies with JVM specs:41*42* If the arrayref refers to an array whose components are of type boolean,43* then the int value is narrowed by taking the bitwise AND of value and 1;44* the result is stored as the component of the array indexed by index45*46* bastore can be array store to either boolean or byte array. To47* figure out which bastore is for boolean array, the following steps are taken:48*49* 1. Normal IL generation pass solves the cases where the array base node is parm50* ,field, or call. It also collects info about if there are stores of boolean51* or bytes array type to any autos. If only one type of array or multi dimension52* array ever appears in the IL trees, the bstorei nodes can only be this one type.53* 2. If arrays of both types exist in the IL trees, the transformer do a traverse54* of the CFG and tries to propagate auto types as best effort. This55* approach might still leave some array's type unknown when there are stores of NULL56* to an auto, or there are loops, or the base array node is from a multinewarray.57* 3. A runtime check is added for the array base node if the type is still unknown58* for any bstorei nodes after the above steps. The check will very likely be folded59* away by global value propagation and the overhead for important method body60* should be minimum.61*/6263#define OPT_DETAILS "O^O BOOL ARRAY STORE TRANSFORMER: "6465static char * getTypeName(TR_YesNoMaybe type, char * buffer)66{67switch (type)68{69case TR_yes:70strcpy(buffer, "[Z");71break;72case TR_no:73strcpy(buffer, "[B");74break;75case TR_maybe:76strcpy(buffer, "unknown type");77break;78}79return buffer;80}8182static void printTypeInfo(TR_BoolArrayStoreTransformer::TypeInfo *typeInfo, TR::Compilation *comp)83{84int localIndex = 0;85for (auto it = typeInfo->begin(); it != typeInfo->end(); it++)86{87if (*it != TR_maybe)88{89char buffer[15];90traceMsg(comp, "( local #%2d: %s ) ", localIndex, getTypeName(*it, buffer));91}92localIndex++;93}94}9596TR_BoolArrayStoreTransformer::TR_BoolArrayStoreTransformer(NodeSet *bstoreiUnknownArrayTypeNodes, NodeSet *bstoreiBoolArrayTypeNodes)97:98_bstoreiUnknownArrayTypeNodes(bstoreiUnknownArrayTypeNodes),99_bstoreiBoolArrayTypeNodes(bstoreiBoolArrayTypeNodes),100_hasBoolArrayAutoOrCheckCast(false),101_hasByteArrayAutoOrCheckCast(false),102_hasVariantArgs(false),103_numLocals(0)104{105_comp = TR::comp();106}107108void TR_BoolArrayStoreTransformer::perform()109{110if (comp()->getOption(TR_TraceILGen))111traceMsg(comp(), "<BoolArrayStoreTransformer>\n");112113if (!_hasVariantArgs)114{115// There is no store to args and bstorei nodes with argument as array base have known type116for (auto it = _bstoreiUnknownArrayTypeNodes->begin(); it != _bstoreiUnknownArrayTypeNodes->end();)117{118TR::Node *bstoreiNode = *it;119it++;120TR::Node *arrayBaseNode = bstoreiNode->getFirstChild()->getFirstChild();121if (arrayBaseNode->getOpCode().hasSymbolReference() && arrayBaseNode->getSymbolReference()->getSymbol()->isParm())122{123if (isBoolArrayNode(arrayBaseNode, false /* parmAsAuto */))124{125if (comp()->getOption(TR_TraceILGen))126traceMsg(comp(), "bstorei node n%dn is [Z from parm type signature\n", bstoreiNode->getGlobalIndex());127_bstoreiBoolArrayTypeNodes->insert(bstoreiNode);128_bstoreiUnknownArrayTypeNodes->erase(bstoreiNode);129}130else if (isByteArrayNode(arrayBaseNode, false /* parmAsAuto */))131{132if (comp()->getOption(TR_TraceILGen))133traceMsg(comp(), "bstorei node n%dn is [B from parm type signature\n", bstoreiNode->getGlobalIndex());134_bstoreiUnknownArrayTypeNodes->erase(bstoreiNode);135}136}137}138}139else140{141// parm are treated as auto as well142ListIterator<TR::ParameterSymbol> parms(&comp()->getMethodSymbol()->getParameterList());143for (TR::ParameterSymbol * p = parms.getFirst(); p; p = parms.getNext())144{145if (isAnyDimensionBoolArrayParm(p))146_hasBoolArrayAutoOrCheckCast = true;147else if (isAnyDimensionByteArrayParm(p))148_hasByteArrayAutoOrCheckCast = true;149}150}151152if (!_bstoreiUnknownArrayTypeNodes->empty())153{154if (_hasByteArrayAutoOrCheckCast && _hasBoolArrayAutoOrCheckCast) // only need to iterate CFG if both byte and boolean array exist155findBoolArrayStoreNodes();156else157{158if (_hasBoolArrayAutoOrCheckCast) // if only boolean array exist then all the bstorei nodes are operating on boolean array159{160if (comp()->getOption(TR_TraceILGen))161traceMsg(comp(), "only boolean array exist as auto or checkcast type\n");162_bstoreiBoolArrayTypeNodes->insert(_bstoreiUnknownArrayTypeNodes->begin(), _bstoreiUnknownArrayTypeNodes->end());163}164else165{166if (comp()->getOption(TR_TraceILGen))167traceMsg(comp(), "only byte array exist as auto or checkcast type\n");168}169_bstoreiUnknownArrayTypeNodes->clear();170}171}172173if (!_bstoreiBoolArrayTypeNodes->empty())174transformBoolArrayStoreNodes();175176if (!_bstoreiUnknownArrayTypeNodes->empty())177transformUnknownTypeArrayStore();178179if (comp()->getOption(TR_TraceILGen))180traceMsg(comp(), "</BoolArrayStoreTransformer>\n");181}182183void TR_BoolArrayStoreTransformer::collectLocals(TR_Array<List<TR::SymbolReference>> *autosListArray)184{185for (int i = 0; autosListArray && i < autosListArray->size(); i++)186{187List<TR::SymbolReference> autosList = (*autosListArray)[i];188ListIterator<TR::SymbolReference> autosIt(&autosList);189for (TR::SymbolReference* symRef = autosIt.getFirst(); symRef; symRef = autosIt.getNext())190{191TR::AutomaticSymbol *p = symRef->getSymbol()->getAutoSymbol();192if (p && p->getDataType() == TR::Address)193{194if (comp()->getOption(TR_TraceILGen))195traceMsg(comp(), "Local #%2d is symbol %p [#n%dn]\n", _numLocals, p, symRef->getReferenceNumber());196p->setLocalIndex(_numLocals++);197}198}199}200}201202/*203* Merge second type info into first one if it has more concrete type for any auto204*/205void TR_BoolArrayStoreTransformer::mergeTypeInfo(TypeInfo *first, TypeInfo *second)206{207bool traceIt = comp()->getOption(TR_TraceILGen);208if (traceIt)209{210traceMsg(comp(), "before merging: ");211printTypeInfo(first, comp());212traceMsg(comp(), "\n");213}214215bool changed = false;216for (int i = 0; i < _numLocals; i++)217{218TR_YesNoMaybe firstType = (*first)[i];219TR_YesNoMaybe secondType = (*second)[i];220if (secondType != TR_maybe)221{222if (firstType == TR_maybe)223{224(*first)[i] = secondType;225changed = true;226}227else if (firstType != secondType )228{229// It doesn't matter which type wins because there must be a checkcast230// or another kill with specific type later on every path reaching a bstorei231if (traceIt)232{233char firstTypeBuffer[15];234char secondTypeBuffer[15];235traceMsg(comp(), "local #%2d has conflict types keep the first type for now: firstType %s, secondType %s\n", i, getTypeName(firstType, firstTypeBuffer), getTypeName(secondType, secondTypeBuffer));236}237}238}239}240241if (changed && traceIt)242{243traceMsg(comp(), "after merging: ");244printTypeInfo(first, comp());245traceMsg(comp(), "\n");246}247}248249void TR_BoolArrayStoreTransformer::findBoolArrayStoreNodes()250{251TR::Region currentRegion(comp()->region());252253ListIterator<TR::ParameterSymbol> parms(&comp()->getMethodSymbol()->getParameterList());254for (TR::ParameterSymbol * p = parms.getFirst(); p; p = parms.getNext())255{256if (p->getDataType() == TR::Address)257{258if (comp()->getOption(TR_TraceILGen))259traceMsg(comp(), "Local #%2d is symbol %p <parm %d>\n", _numLocals, p, p->getSlot());260p->setLocalIndex(_numLocals++);261}262}263collectLocals(comp()->getMethodSymbol()->getAutoSymRefs());264collectLocals(comp()->getMethodSymbol()->getPendingPushSymRefs());265266typedef TR::typed_allocator<std::pair<const int32_t, TypeInfo *>, TR::Region &> ResultAllocator;267typedef std::map<int32_t, TypeInfo *, std::less<int32_t>, ResultAllocator> BlockResultMap;268BlockResultMap blockStartTypeInfos(std::less<int32_t>(), comp()->trMemory()->currentStackRegion());269270/*271* Do a reverse post-order traversal of the CFG as the best effort to figure out types in one traverse272*/273TR::ReversePostorderSnapshotBlockIterator blockIt (comp()->getFlowGraph(), comp());274//Initialize type info for parms for the entry block275if (blockIt.currentBlock())276{277TR::Block *firstBlock = blockIt.currentBlock();278ListIterator<TR::ParameterSymbol> parms(&comp()->getMethodSymbol()->getParameterList());279TypeInfo * typeInfo = NULL;280for (TR::ParameterSymbol * p = parms.getFirst(); p; p = parms.getNext())281{282if (p->getDataType() == TR::Address)283{284TR_YesNoMaybe type = TR_maybe;285if (isBoolArrayParm(p))286type = TR_yes;287else if (isByteArrayParm(p))288type = TR_no;289290if (type != TR_maybe)291{292if (!typeInfo)293typeInfo = new (comp()->trMemory()->currentStackRegion()) TypeInfo(_numLocals, TR_maybe, comp()->trMemory()->currentStackRegion());294(*typeInfo)[p->getLocalIndex()] = type;295}296}297}298299if (typeInfo)300{301blockStartTypeInfos[firstBlock->getNumber()] = typeInfo;302if (comp()->getOption(TR_TraceILGen))303{304traceMsg(comp(), "Entry Block (block_%d) type Info: ", firstBlock->getNumber());305printTypeInfo(typeInfo, comp());306traceMsg(comp(), "\n");307}308}309}310311TR::BlockChecklist visitedBlock(comp());312_NumOfBstoreiNodesToVisit = _bstoreiUnknownArrayTypeNodes->size();313while (blockIt.currentBlock() && _NumOfBstoreiNodesToVisit > 0)314{315TR::Block *block = blockIt.currentBlock();316int32_t blockNum = block->getNumber();317TypeInfo *blockStartTypeInfo = blockStartTypeInfos.find(blockNum) != blockStartTypeInfos.end()? blockStartTypeInfos[blockNum]: NULL;318TypeInfo *blockEndTypeInfo = processBlock(block, blockStartTypeInfo);319visitedBlock.add(block);320TR_SuccessorIterator bi(block);321for (TR::CFGEdge *edge = bi.getFirst(); edge != NULL; edge = bi.getNext())322{323TR::Block *nextBlock = toBlock(edge->getTo());324int32_t nextBlockNum = nextBlock->getNumber();325//propagate auto type info to successor326//if the type info exist for the successor merge with the new one327if (blockEndTypeInfo && !visitedBlock.contains(nextBlock))328{329if (blockStartTypeInfos.find(nextBlockNum) != blockStartTypeInfos.end())330{331if (comp()->getOption(TR_TraceILGen))332traceMsg(comp(), "merging into type info of successor block_%d\n", nextBlockNum);333mergeTypeInfo(blockStartTypeInfos[nextBlockNum], blockEndTypeInfo);334}335else336blockStartTypeInfos[nextBlockNum] = new (currentRegion) TypeInfo(*blockEndTypeInfo);337}338}339++blockIt;340}341}342343/*344* Answer includes multi dimension boolean array345*/346bool TR_BoolArrayStoreTransformer::isAnyDimensionBoolArrayNode(TR::Node *node)347{348return getArrayDimension(node, true /*boolType*/, false /* parmAsAuto*/) >= 1;349}350351/*352* Answer includes multi dimension byte array353*/354bool TR_BoolArrayStoreTransformer::isAnyDimensionByteArrayNode(TR::Node *node)355{356return getArrayDimension(node, false /*boolType*/, false /* parmAsAuto */) >= 1;357}358359360/*361* Answer includes multi dimension boolean array362*/363bool TR_BoolArrayStoreTransformer::isAnyDimensionBoolArrayParm(TR::ParameterSymbol *symbol)364{365int length;366const char *signature = symbol->getTypeSignature(length);367return getArrayDimension(signature, length, true /*boolType*/) >= 1;368}369370/*371* Answer includes multi dimension byte array372*/373bool TR_BoolArrayStoreTransformer::isAnyDimensionByteArrayParm(TR::ParameterSymbol *symbol)374{375int length;376const char *signature = symbol->getTypeSignature(length);377return getArrayDimension(signature, length, false /*boolType*/) >= 1;378}379380/*381* \brief382* Answer includes one dimension boolean array only383*384* \parm node385*386* \parm parmAsAuto387* For a \parm node with parm symbol, the API is used in two different ways because the slot for dead388* parm symbol can be reused for variables of any type. When \parm parmAsAuto is true, parm is389* treated as other auto and the type signature is ignored. When \parm parmAsAuto is false, the390* type signature of the parm is used for telling the actual type.391*/392bool TR_BoolArrayStoreTransformer::isBoolArrayNode(TR::Node *node, bool parmAsAuto)393{394if (parmAsAuto && node->getOpCode().hasSymbolReference() && node->getSymbolReference()->getSymbol()->isParm())395return false;396return getArrayDimension(node, true /*boolType*/, parmAsAuto) == 1;397}398399/*400* \brief401* Answer includes one dimension byte array only402*403* \parm node404*405* \parm parmAsAuto406* For a \parm node with parm symbol, the API is used in two different ways because the slot for dead407* parm symbol can be reused for variables of any type. When \parm parmAsAuto is true, parm is408* treated as other auto and the type signature is ignored. When \parm parmAsAuto is false, the409* type signature of the parm is used for telling the actual type.410*/411bool TR_BoolArrayStoreTransformer::isByteArrayNode(TR::Node *node, bool parmAsAuto)412{413if (parmAsAuto && node->getOpCode().hasSymbolReference() && node->getSymbolReference()->getSymbol()->isParm())414return false;415return getArrayDimension(node, false /*boolType*/, parmAsAuto) == 1;416}417418/*419* Answer includes one dimension boolean array only420*/421bool TR_BoolArrayStoreTransformer::isBoolArrayParm(TR::ParameterSymbol *symbol)422{423int length;424const char *signature = symbol->getTypeSignature(length);425return getArrayDimension(signature, length, true /*boolType*/) == 1;426}427428/*429* Answer includes one dimension byte array only430*/431bool TR_BoolArrayStoreTransformer::isByteArrayParm(TR::ParameterSymbol *symbol)432{433int length;434const char *signature = symbol->getTypeSignature(length);435return getArrayDimension(signature, length, false /*boolType*/) == 1;436}437438int TR_BoolArrayStoreTransformer::getArrayDimension(const char * signature, int length, bool boolType)439{440char expectedTypeChar = boolType? 'Z' : 'B';441return (signature && length >= 2 && signature[length-1] == expectedTypeChar && signature[length-2] == '[') ? length-1: -1;442}443444/*445* \brief446* Get dimension of the array of given type447*448* \parm node449* The node to look at450*451* \parm boolType452* True if asking for boolean array and false if asking for byte array453*454* \return455* The dimension of the array of given type. Return -1 if the node is not the456* given type.457*/458int TR_BoolArrayStoreTransformer::getArrayDimension(TR::Node *node, bool boolType, bool parmAsAuto)459{460int nodeArrayDimension = -1;461if (node->getOpCodeValue() == TR::newarray)462{463int32_t expectedTypeValue = boolType ? 4: 8;464TR::Node *arrayTypeNode = node->getSecondChild();465TR_ASSERT(arrayTypeNode->getOpCode().isLoadConst(), "expect the second child of TR::newarray to be constant " POINTER_PRINTF_FORMAT, arrayTypeNode);466if (arrayTypeNode->getInt() == expectedTypeValue)467nodeArrayDimension = 1;468}469else470{471int length;472const char * signature = node->getTypeSignature(length, stackAlloc, parmAsAuto);473nodeArrayDimension = getArrayDimension(signature, length, boolType);474}475return nodeArrayDimension;476}477478/*479* \brief480* Find all the loads of autos or parms in a subtree and figure out its type481* first time the load is referenced.482*483* \parm node484* The subtree to look at485*486* \parm typeInfo487* The type information of each auto at the current subtree488*489* \parm boolArrayNodes490* Load of autos or parms that are [Z491*492* \parm byteArrayNodes493* Load of autos or parms that are [B494*495* \parm visitedNodes496* All the nodes in the containing block. A node is added to this list the first time seen in the trees497*/498void TR_BoolArrayStoreTransformer::findLoadAddressAutoAndFigureOutType(TR::Node *node, TypeInfo * typeInfo, TR::NodeChecklist &boolArrayNodes, TR::NodeChecklist &byteArrayNodes, TR::NodeChecklist &visitedNodes)499{500if (visitedNodes.contains(node))501return;502503for (int i = 0; i < node->getNumChildren(); i++)504findLoadAddressAutoAndFigureOutType(node->getChild(i), typeInfo, boolArrayNodes, byteArrayNodes, visitedNodes);505506if (node->getType() == TR::Address && node->getOpCode().isLoadDirect() && node->getOpCode().hasSymbolReference() &&507node->getSymbolReference()->getSymbol()->isAutoOrParm() && !visitedNodes.contains(node))508{509TR_YesNoMaybe type = (*typeInfo)[node->getSymbolReference()->getSymbol()->getLocalIndex()];510if (type == TR_yes)511boolArrayNodes.add(node);512else if (type == TR_no)513byteArrayNodes.add(node);514}515visitedNodes.add(node);516}517518/*519* \brief520* This function calculcates the type info of each auto and figure out whether a521* bstorei node is for boolean array522*523* \parm block524* The block to process525*526* \parm blockStartTypeInfo527* The auto type info at block start528*529* \return currentTypeInfo530* The working auto type info531*532* \note533* For each load of auto, record whether the type is boolean array (TR_yes), byte array type534* (TR_no), other or unknown type (TR_maybe). For each store of auto, propagate the type from535* right hand side to the left hand side auto.536*/537TR_BoolArrayStoreTransformer::TypeInfo * TR_BoolArrayStoreTransformer::processBlock(TR::Block *block, TR_BoolArrayStoreTransformer::TypeInfo *blockStartTypeInfo)538{539TR_BoolArrayStoreTransformer::TypeInfo *currentTypeInfo = blockStartTypeInfo;540TR::NodeChecklist boolArrayNodes(comp());541TR::NodeChecklist byteArrayNodes(comp());542TR::NodeChecklist visitedNodes(comp());543544if (comp()->getOption(TR_TraceILGen))545{546traceMsg(comp(), "start processing block_%d: ", block->getNumber());547if (currentTypeInfo)548printTypeInfo(currentTypeInfo, comp());549traceMsg(comp(), "\n");550}551552for (TR::TreeTop *tt = block->getEntry(); tt != block->getExit(); tt = tt->getNextTreeTop())553{554TR::Node *node = tt->getNode();555// find all aload auto from the current tree top first556if (currentTypeInfo)557findLoadAddressAutoAndFigureOutType(node, currentTypeInfo, boolArrayNodes, byteArrayNodes, visitedNodes);558559if (node->getOpCode().isStoreDirect() && node->getSymbolReference()->getSymbol()->isAutoOrParm())560{561TR::Node *rhs = node->getFirstChild();562TR::Symbol *local = node->getSymbolReference()->getSymbol();563if (rhs->getDataType().isAddress())564{565TR_YesNoMaybe newType = TR_maybe;566if (isBoolArrayNode(rhs, _hasVariantArgs) || boolArrayNodes.contains(rhs))567newType = TR_yes;568else if (isByteArrayNode(rhs, _hasVariantArgs) || byteArrayNodes.contains(rhs))569newType = TR_no;570571if (newType != TR_maybe || currentTypeInfo)572{573if (!currentTypeInfo)574currentTypeInfo = new (comp()->trMemory()->currentStackRegion()) TypeInfo(_numLocals, TR_maybe, comp()->trMemory()->currentStackRegion());575if (comp()->getOption(TR_TraceILGen))576{577char newTypeBuffer[15];578char oldTypeBuffer[15];579TR_YesNoMaybe oldType = (*currentTypeInfo)[local->getLocalIndex()];580traceMsg(comp(), "Local #%2d %s -> %s at node n%dn\n", local->getLocalIndex(), getTypeName(oldType, oldTypeBuffer), getTypeName(newType, newTypeBuffer), node->getGlobalIndex());581}582(*currentTypeInfo)[local->getLocalIndex()] = newType;583}584}585}586else if (node->getOpCodeValue() == TR::bstorei && (_bstoreiUnknownArrayTypeNodes->find(node) != _bstoreiUnknownArrayTypeNodes->end()))587{588_NumOfBstoreiNodesToVisit--;589TR_ASSERT(node->getFirstChild()->isInternalPointer(), "node in _bstoreiUnknownArrayTypeNodes can only be array store");590TR::Node *arrayBaseNode = node->getFirstChild()->getFirstChild();591if (boolArrayNodes.contains(arrayBaseNode))592{593if (comp()->getOption(TR_TraceILGen))594{595char buffer[15];596traceMsg(comp(), "bstorei node n%dn is %s\n", node->getGlobalIndex(), getTypeName(TR_yes, buffer));597}598_bstoreiUnknownArrayTypeNodes->erase(node);599_bstoreiBoolArrayTypeNodes->insert(node);600}601else if (byteArrayNodes.contains(arrayBaseNode))602{603if (comp()->getOption(TR_TraceILGen))604{605char buffer[15];606traceMsg(comp(), "bstorei node n%dn is %s\n", node->getGlobalIndex(), getTypeName(TR_no, buffer));607}608_bstoreiUnknownArrayTypeNodes->erase(node);609}610}611else if (node->getOpCode().isCheckCast())612{613TR::Node *typeNode = node->getSecondChild();614TR::Node *checkcastedNode = node->getFirstChild();615if (isBoolArrayNode(typeNode))616{617if (byteArrayNodes.contains(checkcastedNode)) // this can happen when [Z and [B are merged at one point618byteArrayNodes.remove(checkcastedNode);619if (comp()->getOption(TR_TraceILGen))620traceMsg(comp(), "checkcast node n%dn force node n%dn to be [Z\n", node->getGlobalIndex(), checkcastedNode->getGlobalIndex());621boolArrayNodes.add(checkcastedNode);622}623else if (isByteArrayNode(typeNode))624{625if (boolArrayNodes.contains(checkcastedNode)) // this can happen when [Z and [B are merged at one point626boolArrayNodes.remove(checkcastedNode);627if (comp()->getOption(TR_TraceILGen))628traceMsg(comp(), "checkcast node n%dn force node n%dn to be [B\n", node->getGlobalIndex(), checkcastedNode->getGlobalIndex());629byteArrayNodes.add(checkcastedNode);630}631}632}633634if (comp()->getOption(TR_TraceILGen))635{636traceMsg(comp(), "end processing block_%d: ", block->getNumber());637if (currentTypeInfo)638printTypeInfo(currentTypeInfo, comp());639traceMsg(comp(), "\n");640}641642return currentTypeInfo;643}644645/* \brief646* Generating nodes to and the value to be stored to the array with the provided mask647*648* \parm bstoreiNode649*650* \parm int mask651*652* \note653* Transform bstorei node from:654* bstorei655* aladd (internal pointer)656* ...657* value node658*659* to:660* bstorei661* aladd (internal pointer)662* ...663* i2b664* iand665* b2i666* value node667* mask node668*/669static void generateiAndNode(TR::Node *bstoreiNode, TR::Node *mask, TR::Compilation *comp)670{671if (comp->getOption(TR_TraceILGen))672traceMsg(comp, "truncating mask node n%dn\n", mask->getGlobalIndex());673TR::Node *bValueChild = bstoreiNode->getSecondChild();674TR::Node *iValueChild = TR::Node::create(bstoreiNode, TR::b2i, 1, bValueChild);675TR::Node *iandNode = TR::Node::create(bstoreiNode, TR::iand, 2, iValueChild, mask);676TR::Node *i2bNode = TR::Node::create(bstoreiNode, TR::i2b, 1, iandNode);677bstoreiNode->setAndIncChild(1, i2bNode);678bValueChild->decReferenceCount();679}680681/*682* \brief683* For bstorei with unknown type info, and the value to be stored with a mask.684*685* \note686* The mask value is calculated based on runtime array type info:687* mask = (class of array base node == J9class of [Z ? 1: 0)*2 - 1. It's 1 for688* boolean array and -1 (0xFFFFFFFF) for byte array.689690* Mask calculation in tree:691* iadd692* ishl693* acmpeq694* aloadi <vft-symbol>695* => array base node696* aconst [Z J9Class697* 2698* iconst -1 (0xffffffff)699*/700void TR_BoolArrayStoreTransformer::transformUnknownTypeArrayStore()701{702TR_J9VMBase *fej9 = (TR_J9VMBase *)(comp()->fe());703// this check should be deleted after the new AOT work is delivered704if (fej9->isAOT_DEPRECATED_DO_NOT_USE())705return;706//get j9class of [Z707uintptr_t j9class = (uintptr_t) fej9->getClassFromNewArrayType(4);708for (auto it = _bstoreiUnknownArrayTypeNodes->begin(); it != _bstoreiUnknownArrayTypeNodes->end(); it++)709{710TR::Node *bstoreiNode = *it;711dumpOptDetails(comp(), "%s transform value child of bstorei node of unknown type n%dn\n", OPT_DETAILS, bstoreiNode->getGlobalIndex());712TR::Node *arrayBaseNode = bstoreiNode->getFirstChild()->getFirstChild();713TR::Node *vft = TR::Node::createWithSymRef(TR::aloadi, 1, 1, arrayBaseNode, comp()->getSymRefTab()->findOrCreateVftSymbolRef());714TR::Node *aconstNode = TR::Node::aconst(bstoreiNode, j9class);715aconstNode->setIsClassPointerConstant(true);716TR::Node *compareNode = TR::Node::create(arrayBaseNode, TR::acmpeq, 2, vft, aconstNode);717TR::Node *shift1Node = TR::Node::create(TR::ishl, 2 , compareNode, TR::Node::iconst(bstoreiNode, 1));718TR::Node *iandMaskNode = TR::Node::create(TR::iadd, 2 , shift1Node, TR::Node::iconst(bstoreiNode, -1));719generateiAndNode(bstoreiNode, iandMaskNode, comp());720}721}722723void TR_BoolArrayStoreTransformer::transformBoolArrayStoreNodes()724{725for (auto it = _bstoreiBoolArrayTypeNodes->begin(); it != _bstoreiBoolArrayTypeNodes->end(); it++)726{727TR::Node *node = *it;728dumpOptDetails(comp(), "%s truncate value child of bstorei node n%dn to 1 bit\n", OPT_DETAILS, node->getGlobalIndex());729generateiAndNode(node, TR::Node::iconst(node, 1), comp());730}731}732733734