/*******************************************************************************
 * Copyright IBM Corp. and others 2000
 *
 * This program and the accompanying materials are made available under
 * the terms of the Eclipse Public License 2.0 which accompanies this
 * distribution and is available at https://www.eclipse.org/legal/epl-2.0/
 * or the Apache License, Version 2.0 which accompanies this distribution and
 * is available at https://www.apache.org/licenses/LICENSE-2.0.
 *
 * This Source Code may also be made available under the following
 * Secondary Licenses when the conditions for such availability set
 * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
 * General Public License, version 2 with the GNU Classpath
 * Exception [1] and GNU General Public License, version 2 with the
 * OpenJDK Assembly Exception [2].
 *
 * [1] https://www.gnu.org/software/classpath/license.html
 * [2] https://openjdk.org/legal/assembly-exception.html
 *
 * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0
 *******************************************************************************/

#include <limits.h>
#include <math.h>
#include <stdint.h>
#include "j9.h"
#include "j9cfg.h"
#include "j9consts.h"
#include "omrmodroncore.h"
#include "thrdsup.h"
#include "thrtypes.h"
#include "codegen/AheadOfTimeCompile.hpp"
#include "codegen/BackingStore.hpp"
#include "codegen/CodeGenerator.hpp"
#include "codegen/CodeGeneratorUtils.hpp"
#include "codegen/CodeGenerator_inlines.hpp"
#include "codegen/Machine.hpp"
#include "codegen/Linkage.hpp"
#include "codegen/Linkage_inlines.hpp"
#include "codegen/LiveRegister.hpp"
#include "codegen/PPCJNILinkage.hpp"
#include "codegen/PPCPrivateLinkage.hpp"
#include "codegen/Relocation.hpp"
#include "codegen/Snippet.hpp"
#include "codegen/TreeEvaluator.hpp"
#include "compile/ResolvedMethod.hpp"
#include "control/Recompilation.hpp"
#include "control/RecompilationInfo.hpp"
#include "codegen/J9PPCWatchedInstanceFieldSnippet.hpp"
#include "codegen/J9PPCWatchedStaticFieldSnippet.hpp"
#include "env/CHTable.hpp"
#include "env/CompilerEnv.hpp"
#include "env/IO.hpp"
#include "env/jittypes.h"
#include "env/VMJ9.h"
#include "il/DataTypes.hpp"
#include "il/LabelSymbol.hpp"
#include "il/Node.hpp"
#include "il/Node_inlines.hpp"
#include "il/TreeTop.hpp"
#include "il/TreeTop_inlines.hpp"
#include "infra/Annotations.hpp"
#include "infra/ILWalk.hpp"
#include "infra/Bit.hpp"
#include "optimizer/J9TransformUtil.hpp"
#include "optimizer/VectorAPIExpansion.hpp"
#include "p/codegen/ForceRecompilationSnippet.hpp"
#include "p/codegen/GenerateInstructions.hpp"
#include "p/codegen/InterfaceCastSnippet.hpp"
#include "p/codegen/J9PPCSnippet.hpp"
#include "p/codegen/LoadStoreHandler.hpp"
//#include "p/codegen/PPCAheadOfTimeCompile.hpp"
#include "p/codegen/PPCEvaluator.hpp"
#include "p/codegen/PPCInstruction.hpp"
#include "p/codegen/PPCTableOfConstants.hpp"
#include "runtime/CodeCache.hpp"
#include "runtime/Runtime.hpp"
#include "env/VMJ9.h"

#define MAX_PPC_ARRAYCOPY_INLINE 256
#define AIX_NULL_ZERO_AREA_SIZE 256
#define NUM_PICS 3

#define LOCK_INC_DEC_VALUE                                OBJECT_HEADER_LOCK_FIRST_RECURSION_BIT
#define LOCK_RESERVATION_BIT                              OBJECT_HEADER_LOCK_RESERVED

#define LOCK_RES_PRIMITIVE_ENTER_MASK                     (OBJECT_HEADER_LOCK_RECURSION_MASK | OBJECT_HEADER_LOCK_FLC)
#define LOCK_RES_PRIMITIVE_EXIT_MASK                      (OBJECT_HEADER_LOCK_BITS_MASK & ~OBJECT_HEADER_LOCK_RECURSION_MASK)
#define LOCK_RES_NON_PRIMITIVE_ENTER_MASK                 (OBJECT_HEADER_LOCK_RECURSION_MASK & ~OBJECT_HEADER_LOCK_LAST_RECURSION_BIT)
#define LOCK_RES_NON_PRIMITIVE_EXIT_MASK                  (OBJECT_HEADER_LOCK_RECURSION_MASK & ~OBJECT_HEADER_LOCK_FIRST_RECURSION_BIT)

#define LOCK_RES_OWNING_COMPLEMENT                        (OBJECT_HEADER_LOCK_RECURSION_MASK | OBJECT_HEADER_LOCK_FLC)

#define LOCK_NON_PRIMITIVE_ENTER_IGNORE_MASK              ((OBJECT_HEADER_LOCK_RECURSION_MASK & ~OBJECT_HEADER_LOCK_LAST_RECURSION_BIT) | OBJECT_HEADER_LOCK_RESERVED | OBJECT_HEADER_LOCK_FLC)
#define LOCK_NON_PRIMITIVE_EXIT_IGNORE_MASK               (OBJECT_HEADER_LOCK_RECURSION_MASK | OBJECT_HEADER_LOCK_RESERVED | OBJECT_HEADER_LOCK_FLC)

extern TR::Register *addConstantToLong(TR::Node * node, TR::Register *srcReg, int64_t value, TR::Register *trgReg, TR::CodeGenerator *cg);
extern TR::Register *addConstantToInteger(TR::Node * node, TR::Register *trgReg, TR::Register *srcReg, int32_t value, TR::CodeGenerator *cg);

static TR::InstOpCode::Mnemonic getLoadOrStoreFromDataType(TR::CodeGenerator *cg, TR::DataType dt, int32_t elementSize, bool isUnsigned, bool returnLoad);
void loadFieldWatchSnippet(TR::CodeGenerator *cg, TR::Node *node, TR::Snippet *dataSnippet, TR::Register *snippetReg, TR::Register *scratchReg, bool isInstance);
static const char *ppcSupportsReadMonitors = feGetEnv("TR_ppcSupportReadMonitors");

extern uint32_t getPPCCacheLineSize();

static TR::RegisterDependencyConditions *createConditionsAndPopulateVSXDeps(TR::CodeGenerator *cg, int32_t nonVSXDepsCount)
   {
   TR::RegisterDependencyConditions *conditions;
   TR_LiveRegisters *lrVector = cg->getLiveRegisters(TR_VSX_VECTOR);
   bool liveVSXVectorReg = (!lrVector || (lrVector->getNumberOfLiveRegisters() > 0));
   const TR::PPCLinkageProperties& properties = cg->getLinkage()->getProperties();
   if (liveVSXVectorReg)
      {
      int32_t depsCount = 64 + nonVSXDepsCount;
      conditions = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(depsCount, depsCount, cg->trMemory());
      for (int32_t i = TR::RealRegister::FirstFPR; i <= TR::RealRegister::LastFPR; i++)
         {
         if (!properties.getPreserved((TR::RealRegister::RegNum) i))
            {
            TR::addDependency(conditions, NULL, (TR::RealRegister::RegNum) i, TR_FPR, cg);
            }
         }
      for (int32_t i = TR::RealRegister::FirstVRF; i <= TR::RealRegister::LastVRF; i++)
         {
         if (!properties.getPreserved((TR::RealRegister::RegNum) i))
            {
            TR::addDependency(conditions, NULL, (TR::RealRegister::RegNum) i, TR_VRF, cg);
            }
         }
      }
   else
      {
      TR_LiveRegisters *lrScalar = cg->getLiveRegisters(TR_VSX_SCALAR);
      if (!lrScalar || (lrScalar->getNumberOfLiveRegisters() > 0))
         {
         int32_t depsCount = 32 + nonVSXDepsCount;
         conditions = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(depsCount, depsCount, cg->trMemory());
         for (int32_t i = TR::RealRegister::FirstVRF; i <= TR::RealRegister::LastVRF; i++)
            {
            if (!properties.getPreserved((TR::RealRegister::RegNum) i))
               {
               TR::addDependency(conditions, NULL, (TR::RealRegister::RegNum) i, TR_VRF, cg);
               }
            }
         }
      else
         {
         conditions = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(nonVSXDepsCount, nonVSXDepsCount, cg->trMemory());
         }
      }
   return conditions;
   }

static TR::Instruction *genNullTest(TR::Node *node, TR::Register *objectReg, TR::Register *crReg, TR::CodeGenerator *cg, TR::Instruction *cursor = NULL)
   {
   cursor = generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpli, node, crReg, objectReg, NULLVALUE, cursor);
   return cursor;
   }

static inline TR::Instruction *genNullTest(TR::Node *node, TR::Register *objectReg, TR::Register *crReg, TR::Register *scratchReg, TR::Instruction *cursor, TR::CodeGenerator *cg)
   {
   TR::Instruction *iRet;
   iRet = generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpli, node, crReg, objectReg, NULLVALUE, cursor);
   return (iRet);
   }

static void genInlineTest(TR::Node * node, TR_OpaqueClassBlock* castClassAddr, TR_OpaqueClassBlock** guessClassArray, uint8_t num_PICS, TR::Register * objClassReg,
      TR::Register * resultReg, TR::Register * cndReg, TR::Register * scratch1Reg, TR::Register * scratch2Reg, bool checkCast, bool needsResult, TR::LabelSymbol * falseLabel,
      TR::LabelSymbol * trueLabel, TR::LabelSymbol * doneLabel, TR::LabelSymbol * callLabel, bool testCastClassIsSuper, TR::Instruction *iCursor, TR::CodeGenerator * cg)
   {
   TR::Compilation * comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (comp->fe());
   dumpOptDetails(comp, "inline test with %d guess classes\n", num_PICS);
   TR_ASSERT(num_PICS != 0, "genInlineTest is called with at least 1 guess class\n");

   TR::LabelSymbol *snippetLabel = generateLabelSymbol(cg);

   // TODO: we pass callLabel twice in TR::PPCInterfaceCastSnippet. However, this is because the first one is used as
   // the restart point on TR::PPCInterfaceCastSnippet, and the second as the reference to the call to the helper function
   // this lets us have a more generic Snippet. This can be simplified later on, should it be needed.
   //
   TR::PPCInterfaceCastSnippet * guessCacheSnippet = new (cg->trHeapMemory()) TR::PPCInterfaceCastSnippet(cg, node, callLabel, snippetLabel, trueLabel, falseLabel, doneLabel,
         callLabel, testCastClassIsSuper, checkCast, TR::Compiler->om.offsetOfObjectVftField(), offsetof(J9Class, castClassCache), needsResult);
   cg->addSnippet(guessCacheSnippet);

   uint8_t i;
   bool result_bool;
   int32_t result_value;
   TR::LabelSymbol *result_label;
   for (i = 0; i < num_PICS - 1; i++)
      {
      // Load the cached value in scratch1Reg
      if (comp->compileRelocatableCode() && comp->getOption(TR_UseSymbolValidationManager))
         {
         TR::StaticSymbol *sym = TR::StaticSymbol::create(comp->trHeapMemory(), TR::Address);
         sym->setStaticAddress(guessClassArray[i]);
         sym->setClassObject();

         iCursor = loadAddressConstant(cg, true, node, (intptr_t) new (comp->trHeapMemory()) TR::SymbolReference(comp->getSymRefTab(), sym), scratch1Reg, iCursor, false, TR_ClassAddress);
         }
      else
         {
         iCursor = loadAddressConstant(cg, comp->compileRelocatableCode(), node, (intptr_t) (guessClassArray[i]), scratch1Reg, iCursor);
         }
      result_bool = fej9->instanceOfOrCheckCast((J9Class*) guessClassArray[i], (J9Class*) castClassAddr);
      int32_t result_value = result_bool ? 1 : 0;
      result_label = (falseLabel != trueLabel) ? (result_bool ? trueLabel : falseLabel) : doneLabel;
      iCursor = generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, cndReg, objClassReg, scratch1Reg, iCursor);
      if (needsResult)
         iCursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, result_value, iCursor);
      iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, result_label, cndReg, iCursor);
      }

   // Load the cached value in scratch1Reg
   if (comp->compileRelocatableCode() && comp->getOption(TR_UseSymbolValidationManager))
      {
      TR::StaticSymbol *sym = TR::StaticSymbol::create(comp->trHeapMemory(), TR::Address);
      sym->setStaticAddress(guessClassArray[num_PICS - 1]);
      sym->setClassObject();

      iCursor = loadAddressConstant(cg, true, node, (intptr_t) new (comp->trHeapMemory()) TR::SymbolReference(comp->getSymRefTab(), sym), scratch1Reg, iCursor, false, TR_ClassAddress);
      }
   else
      {
      iCursor = loadAddressConstant(cg, comp->compileRelocatableCode(), node, (intptr_t) (guessClassArray[num_PICS - 1]), scratch1Reg, iCursor);
      }
   iCursor = generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, cndReg, objClassReg, scratch1Reg, iCursor);
   iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, snippetLabel, cndReg, iCursor);
   result_bool = fej9->instanceOfOrCheckCast((J9Class*) guessClassArray[num_PICS - 1], (J9Class*) castClassAddr);
   result_value = result_bool ? 1 : 0;
   result_label = (falseLabel != trueLabel) ? (result_bool ? trueLabel : falseLabel) : doneLabel;
   if (needsResult)
      {
      iCursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, result_value, iCursor);
      }
   iCursor = generateLabelInstruction(cg, TR::InstOpCode::b, node, result_label, iCursor);
   return;
   }

static TR::Register *nonFixedDependency(TR::RegisterDependencyConditions *conditions, TR::Register *nonFixedReg, int32_t *depIndex, TR_RegisterKinds kind, bool excludeGR0,
      TR::CodeGenerator *cg)
   {
   int32_t index = *depIndex;

   if (nonFixedReg == NULL)
      nonFixedReg = cg->allocateRegister(kind);

   TR::addDependency(conditions, nonFixedReg, TR::RealRegister::NoReg, kind, cg);

   if (excludeGR0)
      {
      conditions->getPreConditions()->getRegisterDependency(index)->setExcludeGPR0();
      conditions->getPostConditions()->getRegisterDependency(index)->setExcludeGPR0();
      }

   *depIndex += 1;
   return nonFixedReg;
   }

static void reviveResultRegister(TR::Register *realResult, TR::Register *deadRes, TR::CodeGenerator *cg)
   {
   TR_RegisterKinds kind = realResult->getKind();
   TR_LiveRegisters *liveRegs = cg->getLiveRegisters(kind);

   if (deadRes->isLive())
      deadRes->getLiveRegisterInfo()->decNodeCount();
   cg->stopUsingRegister(deadRes);

   if (liveRegs != NULL)
      {
      liveRegs->addRegister(realResult);
      }
   }

static int32_t numberOfRegisterCandidate(TR::CodeGenerator *cg, TR::Node *depNode, TR_RegisterKinds kind)
   {
   int32_t idx, result = 0;

   for (idx = 0; idx < depNode->getNumChildren(); idx++)
      {
      TR::Node *child = depNode->getChild(idx);
      TR::Register *reg;

      if (child->getOpCodeValue() == TR::PassThrough)
         child = child->getFirstChild();
      reg = child->getRegister();
      if (reg != NULL && reg->getKind() == kind)
         {
         result += 1;
         if (kind == TR_GPR && cg->comp()->target().is32Bit() && child->getType().isInt64())
            result += 1;
         }
      }
   return (result);
   }

// ----------------------------------------------------------------------------



/*
 * J9 PPC specific tree evaluator table overrides
 */
extern void TEMPORARY_initJ9PPCTreeEvaluatorTable(TR::CodeGenerator *cg)
   {
   OMR::TreeEvaluatorFunctionPointerTable tet = cg->getTreeEvaluatorTable();

   tet[TR::awrtbar] = TR::TreeEvaluator::awrtbarEvaluator;
   tet[TR::awrtbari] = TR::TreeEvaluator::awrtbariEvaluator;
   tet[TR::monent] = TR::TreeEvaluator::monentEvaluator;
   tet[TR::monexit] = TR::TreeEvaluator::monexitEvaluator;
   tet[TR::monexitfence] = TR::TreeEvaluator::monexitfenceEvaluator;
   tet[TR::asynccheck] = TR::TreeEvaluator::asynccheckEvaluator;
   tet[TR::instanceof] = TR::TreeEvaluator::instanceofEvaluator;
   tet[TR::checkcast] = TR::TreeEvaluator::checkcastEvaluator;
   tet[TR::checkcastAndNULLCHK] = TR::TreeEvaluator::checkcastAndNULLCHKEvaluator;
   tet[TR::New] = TR::TreeEvaluator::newObjectEvaluator;
   tet[TR::variableNew] = TR::TreeEvaluator::newObjectEvaluator;
   tet[TR::newarray] = TR::TreeEvaluator::newArrayEvaluator;
   tet[TR::anewarray] = TR::TreeEvaluator::anewArrayEvaluator;
   tet[TR::variableNewArray] = TR::TreeEvaluator::anewArrayEvaluator;
   tet[TR::multianewarray] = TR::TreeEvaluator::multianewArrayEvaluator;
   tet[TR::arraylength] = TR::TreeEvaluator::arraylengthEvaluator;
   tet[TR::ResolveCHK] = TR::TreeEvaluator::resolveCHKEvaluator;
   tet[TR::DIVCHK] = TR::TreeEvaluator::DIVCHKEvaluator;
   tet[TR::BNDCHK] = TR::TreeEvaluator::BNDCHKEvaluator;
   tet[TR::ArrayCopyBNDCHK] = TR::TreeEvaluator::ArrayCopyBNDCHKEvaluator;
   tet[TR::BNDCHKwithSpineCHK] = TR::TreeEvaluator::BNDCHKwithSpineCHKEvaluator;
   tet[TR::SpineCHK] = TR::TreeEvaluator::BNDCHKwithSpineCHKEvaluator;
   tet[TR::ArrayStoreCHK] = TR::TreeEvaluator::ArrayStoreCHKEvaluator;
   tet[TR::ArrayCHK] = TR::TreeEvaluator::ArrayCHKEvaluator;
   tet[TR::MethodEnterHook] = TR::TreeEvaluator::conditionalHelperEvaluator;
   tet[TR::MethodExitHook] = TR::TreeEvaluator::conditionalHelperEvaluator;
   tet[TR::allocationFence] = TR::TreeEvaluator::flushEvaluator;
   tet[TR::loadFence] = TR::TreeEvaluator::flushEvaluator;
   tet[TR::storeFence] = TR::TreeEvaluator::flushEvaluator;
   tet[TR::fullFence] = TR::TreeEvaluator::flushEvaluator;

   tet[TR::icall]  = TR::TreeEvaluator::directCallEvaluator;
   tet[TR::lcall]  = TR::TreeEvaluator::directCallEvaluator;
   tet[TR::fcall]  = TR::TreeEvaluator::directCallEvaluator;
   tet[TR::dcall]  = TR::TreeEvaluator::directCallEvaluator;
   tet[TR::acall]  = TR::TreeEvaluator::directCallEvaluator;
   tet[TR::call]   = TR::TreeEvaluator::directCallEvaluator;
   tet[TR::vcall] = TR::TreeEvaluator::directCallEvaluator;

   tet[TR::tstart] = TR::TreeEvaluator::tstartEvaluator;
   tet[TR::tfinish] = TR::TreeEvaluator::tfinishEvaluator;
   tet[TR::tabort] = TR::TreeEvaluator::tabortEvaluator;

   tet[TR::NULLCHK] = TR::TreeEvaluator::NULLCHKEvaluator;
   tet[TR::ResolveAndNULLCHK] = TR::TreeEvaluator::resolveAndNULLCHKEvaluator;
   }


static void
VMoutlinedHelperWrtbarEvaluator(
      TR::Node *node,
      TR::Register *srcObjectReg,
      TR::Register *dstObjectReg,
      bool srcIsNonNull,
      TR::CodeGenerator *cg)
   {
   TR::Compilation * comp = cg->comp();
   const auto gcMode = TR::Compiler->om.writeBarrierType();

   if (gcMode == gc_modron_wrtbar_none)
      return;

   const bool doWrtbar = gcMode == gc_modron_wrtbar_satb || gcMode == gc_modron_wrtbar_oldcheck || gcMode == gc_modron_wrtbar_cardmark_and_oldcheck || gcMode == gc_modron_wrtbar_always
         || comp->getOptions()->realTimeGC();

   const bool doCardMark = !node->isNonHeapObjectWrtBar() && (gcMode == gc_modron_wrtbar_cardmark || gcMode == gc_modron_wrtbar_cardmark_and_oldcheck || gcMode == gc_modron_wrtbar_cardmark_incremental);

   TR::CodeCache *codeCache = cg->getCodeCache();
   TR::LabelSymbol *doneWrtbarLabel = generateLabelSymbol(cg);
   TR::Register *srcNullCondReg;

   TR_PPCScratchRegisterDependencyConditions deps;

   // Clobbered by the helper
   deps.addDependency(cg, NULL, TR::RealRegister::cr0);
   deps.addDependency(cg, NULL, TR::RealRegister::cr1);

   if (!srcIsNonNull)
      {
      srcNullCondReg = cg->allocateRegister(TR_CCR);
      generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpli, node, srcNullCondReg, srcObjectReg, NULLVALUE);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneWrtbarLabel, srcNullCondReg);
      }

   if (doWrtbar)
      {
      deps.addDependency(cg, dstObjectReg, TR::RealRegister::gr3);
      deps.addDependency(cg, srcObjectReg, TR::RealRegister::gr4);
      // Clobbered by the helper
      deps.addDependency(cg, NULL, TR::RealRegister::gr5);
      deps.addDependency(cg, NULL, TR::RealRegister::gr6);
      deps.addDependency(cg, NULL, TR::RealRegister::gr11);

      TR_CCPreLoadedCode helper = doCardMark ? TR_writeBarrierAndCardMark : TR_writeBarrier;
      uintptr_t helperAddr = (uintptr_t) codeCache->getCCPreLoadedCodeAddress(helper, cg);
      TR::SymbolReference *symRef = comp->getSymRefTab()->findOrCreatePerCodeCacheHelperSymbolRef(helper, helperAddr);

      TR::Instruction *gcPoint = generateDepImmSymInstruction(cg, TR::InstOpCode::bl, node, helperAddr, new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, 0, cg->trMemory()),
            symRef);
      gcPoint->PPCNeedsGCMap(0xFFFFFFFF);
      }
   else
      {
      TR_ASSERT(doCardMark, "Expecting at least one write barrier to be performed");

      deps.addDependency(cg, dstObjectReg, TR::RealRegister::gr3);
      // Clobbered by the helper
      deps.addDependency(cg, NULL, TR::RealRegister::gr4);
      deps.addDependency(cg, NULL, TR::RealRegister::gr5);
      deps.addDependency(cg, NULL, TR::RealRegister::gr11);

      uintptr_t helperAddr = (uintptr_t) codeCache->getCCPreLoadedCodeAddress(TR_cardMark, cg);
      TR::SymbolReference *symRef = comp->getSymRefTab()->findOrCreatePerCodeCacheHelperSymbolRef(TR_cardMark, helperAddr);
      generateDepImmSymInstruction(cg, TR::InstOpCode::bl, node, helperAddr, new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, 0, cg->trMemory()), symRef);
      }

   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, doneWrtbarLabel, TR_PPCScratchRegisterDependencyConditions::createDependencyConditions(cg, NULL, &deps));

   if (!srcIsNonNull)
      cg->stopUsingRegister(srcNullCondReg);

   cg->machine()->setLinkRegisterKilled(true);
   cg->setHasCall();
   }

TR::Register *outlinedHelperWrtbarEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR::Register *srcObjectReg = cg->gprClobberEvaluate(node->getFirstChild());
   TR::Register *dstObjectReg = cg->gprClobberEvaluate(node->getSecondChild());
   TR::Compilation* comp = cg->comp();

   // Under real-time, store happens after the wrtbar
   // For other GC modes, store happens before the wrtbar
   if (comp->getOptions()->realTimeGC())
      {
      }
   else
      {
      TR::LoadStoreHandler::generateStoreNodeSequence(cg, srcObjectReg, node, TR::InstOpCode::Op_st, TR::Compiler->om.sizeofReferenceAddress());

      if (!node->getFirstChild()->isNull())
         VMoutlinedHelperWrtbarEvaluator(node, srcObjectReg, dstObjectReg, node->getFirstChild()->isNonNull(), cg);
      }

   cg->decReferenceCount(node->getFirstChild());
   cg->decReferenceCount(node->getSecondChild());

   if (srcObjectReg != node->getFirstChild()->getRegister())
      cg->stopUsingRegister(srcObjectReg);
   if (dstObjectReg != node->getSecondChild()->getRegister())
      cg->stopUsingRegister(dstObjectReg);

   return NULL;
   }

static int32_t getOffsetOfJ9ObjectFlags()
   {
#if defined(J9VM_INTERP_FLAGS_IN_CLASS_SLOT)
#if defined(TR_TARGET_64BIT)
   if (!TR::Compiler->om.compressObjectReferences())
#if defined(__LITTLE_ENDIAN__)
      return TMP_OFFSETOF_J9OBJECT_CLAZZ;
#else
      return TMP_OFFSETOF_J9OBJECT_CLAZZ + 4;
#endif
#endif
   return TMP_OFFSETOF_J9OBJECT_CLAZZ;
#else
#if defined(TMP_OFFSETOF_J9OBJECT_FLAGS)
   return TMP_OFFSETOF_J9OBJECT_FLAGS;
#else
   return 0;
#endif
#endif
   }

static void VMnonNullSrcWrtBarCardCheckEvaluator(TR::Node *node, TR::Register *srcReg, TR::Register *dstReg, TR::Register *condReg, TR::Register *temp1Reg, TR::Register *temp2Reg,
      TR::Register *temp3Reg, TR::Register *temp4Reg, TR::LabelSymbol *doneLabel, TR::RegisterDependencyConditions *deps,
      bool isCompressedRef, TR::CodeGenerator *cg, bool flagsInTemp1 = false)
   {
   // non-heap objects cannot be marked
   // Make sure we really should be here
   TR::Compilation *comp = cg->comp();
   TR::Options *options = comp->getOptions();
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());
   auto gcMode = TR::Compiler->om.writeBarrierType();
   TR::LabelSymbol *callLabel = generateLabelSymbol(cg);
   TR::Node *wrtbarNode = NULL;
   TR::SymbolReference *wbRef;
   bool definitelyHeapObject = false, definitelyNonHeapObject = false;
   bool doCrdMrk = (gcMode == gc_modron_wrtbar_cardmark_and_oldcheck);
   bool doWrtBar = (gcMode == gc_modron_wrtbar_satb || gcMode == gc_modron_wrtbar_oldcheck || gcMode == gc_modron_wrtbar_cardmark_and_oldcheck || gcMode == gc_modron_wrtbar_always
         || comp->getOptions()->realTimeGC());
   bool temp3RegIsNull = (temp3Reg == NULL);

   //registers for old/new space boundaries
   TR::Register *bound1 = cg->allocateRegister();
   TR::Register *bound2 = cg->allocateRegister();

   deps->addPostCondition(bound1, TR::RealRegister::NoReg);
   deps->addPostCondition(bound2, TR::RealRegister::NoReg);

   TR_ASSERT(doWrtBar == true, "VMnonNullSrcWrtBarCardCheckEvaluator: Invalid call to VMnonNullSrcWrtBarCardCheckEvaluator\n");

   if (temp3RegIsNull)
      {
      temp3Reg = cg->allocateRegister();
      TR::addDependency(deps, temp3Reg, TR::RealRegister::NoReg, TR_GPR, cg);
      }

   if (node->getOpCodeValue() == TR::awrtbari || node->getOpCodeValue() == TR::awrtbar)
      wrtbarNode = node;
   else if (node->getOpCodeValue() == TR::ArrayStoreCHK)
      wrtbarNode = node->getFirstChild();

   if (wrtbarNode != NULL)
      {
      definitelyHeapObject = wrtbarNode->isHeapObjectWrtBar();
      definitelyNonHeapObject = wrtbarNode->isNonHeapObjectWrtBar();
      }

   if (gcMode == gc_modron_wrtbar_cardmark_and_oldcheck)
      wbRef = comp->getSymRefTab()->findOrCreateWriteBarrierStoreGenerationalAndConcurrentMarkSymbolRef(comp->getMethodSymbol());
   else if (gcMode == gc_modron_wrtbar_oldcheck)
      wbRef = comp->getSymRefTab()->findOrCreateWriteBarrierStoreGenerationalSymbolRef(comp->getMethodSymbol());
   else if (comp->getOptions()->realTimeGC())
      {
      if (wrtbarNode->getOpCodeValue() == TR::awrtbar || wrtbarNode->isUnsafeStaticWrtBar())
         wbRef = comp->getSymRefTab()->findOrCreateWriteBarrierClassStoreRealTimeGCSymbolRef(comp->getMethodSymbol());
      else
         wbRef = comp->getSymRefTab()->findOrCreateWriteBarrierStoreRealTimeGCSymbolRef(comp->getMethodSymbol());
      }
   else
      wbRef = comp->getSymRefTab()->findOrCreateWriteBarrierStoreSymbolRef(comp->getMethodSymbol());

   if (gcMode != gc_modron_wrtbar_always && !comp->getOptions()->realTimeGC())
      {
      bool inlineCrdmrk = (doCrdMrk && !definitelyNonHeapObject);
      // object header flags now occupy 4bytes (instead of 8) on 64-bit. Keep it in temp1Reg for following checks.

      TR::Register *metaReg = cg->getMethodMetaDataRegister();

      TR::LabelSymbol* startICF = generateLabelSymbol(cg);
      startICF->setStartInternalControlFlow();

      // dstReg - heapBaseForBarrierRange0
      generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, bound1,
            TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, heapBaseForBarrierRange0), TR::Compiler->om.sizeofReferenceAddress()));
      generateTrg1Src2Instruction(cg, TR::InstOpCode::subf, node, temp3Reg, bound1, dstReg);

      // if (temp3Reg >= heapSizeForBarrierRage0), object not in the tenured area
      generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, bound2,
            TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, heapSizeForBarrierRange0), TR::Compiler->om.sizeofReferenceAddress()));
      generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, condReg, temp3Reg, bound2);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bge, node, doneLabel, condReg);

      if (!flagsInTemp1)
         generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, temp1Reg, TR::MemoryReference::createWithDisplacement(cg, dstReg, getOffsetOfJ9ObjectFlags(), 4));

      if (inlineCrdmrk)
         {
         TR::LabelSymbol *noChkLabel = NULL;

         // Balanced policy must always dirty the card table.
         //
         if (gcMode != gc_modron_wrtbar_cardmark_incremental)
            {
            noChkLabel = generateLabelSymbol(cg);

            generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp2Reg,
                  TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, privateFlags), TR::Compiler->om.sizeofReferenceAddress()));

            // The value for J9_PRIVATE_FLAGS_CONCURRENT_MARK_ACTIVE is a generated value when VM code is created
            // At the moment we are safe here, but it is better to be careful and avoid any unexpected behaviour
            // Make sure this falls within the scope of andis
            //
            TR_ASSERT(J9_PRIVATE_FLAGS_CONCURRENT_MARK_ACTIVE >= 0x00010000 && J9_PRIVATE_FLAGS_CONCURRENT_MARK_ACTIVE <= 0x80000000,
                  "Concurrent mark active Value assumption broken.");
            generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andis_r, node, temp2Reg, temp2Reg, condReg, J9_PRIVATE_FLAGS_CONCURRENT_MARK_ACTIVE >> 16);

            //start of control flow
            generateLabelInstruction(cg, TR::InstOpCode::label, node, startICF);
            generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, noChkLabel, condReg);
            }

#if defined(TR_TARGET_64BIT)
         uintptr_t card_size_shift = trailingZeroes((uint64_t)options->getGcCardSize());
#else
         uintptr_t card_size_shift = trailingZeroes((uint32_t) options->getGcCardSize());
#endif

         // dirty(activeCardTableBase + temp3Reg >> card_size_shift)
         generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp2Reg,
               TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, activeCardTableBase), TR::Compiler->om.sizeofReferenceAddress()));
#if defined(TR_TARGET_64BIT)
         generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldicl, node, temp3Reg, temp3Reg, 64-card_size_shift, (CONSTANT64(1)<<(64-card_size_shift)) - CONSTANT64(1));
#else
         generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, temp3Reg, temp3Reg, 32 - card_size_shift, ((uint32_t) 0xFFFFFFFF) >> card_size_shift);
#endif
         generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, temp4Reg, CARD_DIRTY);
         generateMemSrc1Instruction(cg, TR::InstOpCode::stb, node, TR::MemoryReference::createWithIndexReg(cg, temp2Reg, temp3Reg, 1), temp4Reg);

         if (noChkLabel)
         {
            //end of control flow
            noChkLabel->setEndInternalControlFlow();
            generateLabelInstruction(cg, TR::InstOpCode::label, node, noChkLabel);
         }

         if (gcMode == gc_modron_wrtbar_cardmark_and_oldcheck)
            {
            //check for src in new space
            generateTrg1Src2Instruction(cg, TR::InstOpCode::subf, node, temp2Reg, bound1, srcReg);

            generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, condReg, temp2Reg, bound2);
            generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, doneLabel, condReg);
            }
         }
      else
         {
         if (gcMode == gc_modron_wrtbar_cardmark_and_oldcheck || gcMode == gc_modron_wrtbar_oldcheck)
            {
            //check for src in new space
            generateTrg1Src2Instruction(cg, TR::InstOpCode::subf, node, temp3Reg, bound1, srcReg);
            generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, condReg, temp3Reg, bound2);
            generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, doneLabel, condReg);
            }
         }

      TR::Instruction * i = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, temp2Reg, temp1Reg, condReg, J9_OBJECT_HEADER_REMEMBERED_MASK_FOR_TEST);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, doneLabel, condReg);
      }
   else if (comp->getOptions()->realTimeGC())
      {
      if (!comp->getOption(TR_DisableInlineWriteBarriersRT))
         {
         // check if barrier is enabled: if disabled then branch around the rest

         TR::MemoryReference *fragmentParentMR = TR::MemoryReference::createWithDisplacement(cg, cg->getMethodMetaDataRegister(),
               fej9->thisThreadRememberedSetFragmentOffset() + fej9->getFragmentParentOffset(), TR::Compiler->om.sizeofReferenceAddress());
         generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp2Reg, fragmentParentMR);

         TR::MemoryReference *globalFragmentIDMR = TR::MemoryReference::createWithDisplacement(cg, temp2Reg, fej9->getRememberedSetGlobalFragmentOffset(), TR::Compiler->om.sizeofReferenceAddress());
         generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp1Reg, globalFragmentIDMR);

         // if barrier not enabled, nothing to do
         generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpi, node, condReg, temp1Reg, NULLVALUE);
         generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, condReg);

         // now check if barrier absolutely necessary, according to GC: if it is, then make sure we go out-of-line
         // if the global fragment index and local fragment index don't match, go to the snippet
         TR::MemoryReference *localFragmentIndexMR = TR::MemoryReference::createWithDisplacement(cg, cg->getMethodMetaDataRegister(),
               fej9->thisThreadRememberedSetFragmentOffset() + fej9->getLocalFragmentOffset(), TR::Compiler->om.sizeofReferenceAddress());
         generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp2Reg, localFragmentIndexMR);
         generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpi, node, condReg, temp2Reg, NULLVALUE);
         generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, callLabel, condReg);

         // null test on the reference we're about to store over: if it is null goto doneLabel
         TR::InstOpCode::Mnemonic opCode = TR::InstOpCode::lwz;
         int32_t size = 4;
         if (comp->target().is64Bit() && !isCompressedRef)
            {
            opCode = TR::InstOpCode::ld;
            size = 8;
            }
         generateTrg1MemInstruction(cg, opCode, node, temp1Reg, TR::MemoryReference::createWithDisplacement(cg, temp3Reg, 0, size));
         generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpi, node, condReg, temp1Reg, NULLVALUE);
         generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, condReg);

         generateLabelInstruction(cg, TR::InstOpCode::label, node, callLabel);
         }
      }

   generateDepImmSymInstruction(cg, TR::InstOpCode::bl, node, (uintptr_t) wbRef->getSymbol()->castToMethodSymbol()->getMethodAddress(),
         new (cg->trHeapMemory()) TR::RegisterDependencyConditions((uint8_t) 0, 0, cg->trMemory()), wbRef, NULL);
   cg->machine()->setLinkRegisterKilled(true);

   cg->stopUsingRegister(bound1);
   cg->stopUsingRegister(bound2);

   if (temp3RegIsNull && temp3Reg)
      cg->stopUsingRegister(temp3Reg);
   }

static void VMCardCheckEvaluator(TR::Node *node, TR::Register *dstReg, TR::Register *condReg, TR::Register *temp1Reg, TR::Register *temp2Reg, TR::Register *temp3Reg,
      TR::RegisterDependencyConditions *deps, TR::CodeGenerator *cg)
   {
   TR::Compilation * comp = cg->comp();
   // non-heap objects cannot be marked
   // Make sure we really should be here
   TR::Options *options = comp->getOptions();
   TR::Node *wrtbarNode = NULL;
   bool definitelyHeapObject = false, definitelyNonHeapObject = false;
   auto gcMode = TR::Compiler->om.writeBarrierType();

   if (node->getOpCodeValue() == TR::awrtbari || node->getOpCodeValue() == TR::awrtbar)
      wrtbarNode = node;
   else if (node->getOpCodeValue() == TR::ArrayStoreCHK)
      wrtbarNode = node->getFirstChild();

   if (wrtbarNode != NULL)
      {
      definitelyHeapObject = wrtbarNode->isHeapObjectWrtBar();
      definitelyNonHeapObject = wrtbarNode->isNonHeapObjectWrtBar();
      }

   TR_ASSERT((gcMode == gc_modron_wrtbar_cardmark || gcMode == gc_modron_wrtbar_cardmark_and_oldcheck || gcMode == gc_modron_wrtbar_cardmark_incremental) && !definitelyNonHeapObject,
         "VMCardCheckEvaluator: Invalid call to cardCheckEvaluator\n");

   if (!definitelyNonHeapObject)
      {
      TR::Register *metaReg = cg->getMethodMetaDataRegister();
      TR::LabelSymbol *noChkLabel = generateLabelSymbol(cg);

      // Balanced policy must always dirty the card table.
      //
      if (gcMode != gc_modron_wrtbar_cardmark_incremental)
         {
         generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp1Reg,
               TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, privateFlags), TR::Compiler->om.sizeofReferenceAddress()));

         // The value for J9_PRIVATE_FLAGS_CONCURRENT_MARK_ACTIVE is a generated value when VM code is created
         // At the moment we are safe here, but it is better to be careful and avoid any unexpected behaviour
         // Make sure this falls within the scope of andis
         //
         TR_ASSERT(J9_PRIVATE_FLAGS_CONCURRENT_MARK_ACTIVE >= 0x00010000 && J9_PRIVATE_FLAGS_CONCURRENT_MARK_ACTIVE <= 0x80000000,
               "Concurrent mark active Value assumption broken.");
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andis_r, node, temp1Reg, temp1Reg, condReg, J9_PRIVATE_FLAGS_CONCURRENT_MARK_ACTIVE >> 16);
         generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, noChkLabel, condReg);
         }

#if defined(TR_TARGET_64BIT)
      uintptr_t card_size_shift = trailingZeroes((uint64_t)options->getGcCardSize());
#else
      uintptr_t card_size_shift = trailingZeroes((uint32_t) options->getGcCardSize());
#endif

      // temp3Reg = dstReg - heapBaseForBarrierRange0
      generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp3Reg,
            TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, heapBaseForBarrierRange0), TR::Compiler->om.sizeofReferenceAddress()));
      generateTrg1Src2Instruction(cg, TR::InstOpCode::subf, node, temp3Reg, temp3Reg, dstReg);

      if (!definitelyHeapObject)
         {
         // if (temp3Reg >= heapSizeForBarrierRage0), object not in the heap
         generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp1Reg,
               TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, heapSizeForBarrierRange0), TR::Compiler->om.sizeofReferenceAddress()));
         generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, condReg, temp3Reg, temp1Reg);
         generateConditionalBranchInstruction(cg, TR::InstOpCode::bge, node, noChkLabel, condReg);
         }

      // dirty(activeCardTableBase + temp3Reg >> card_size_shift)
      generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp1Reg,
            TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, activeCardTableBase), TR::Compiler->om.sizeofReferenceAddress()));
#if defined(TR_TARGET_64BIT)
      generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldicl, node, temp3Reg, temp3Reg, 64-card_size_shift, (CONSTANT64(1)<<(64-card_size_shift)) - CONSTANT64(1));
#else
      generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, temp3Reg, temp3Reg, 32 - card_size_shift, ((uint32_t) 0xFFFFFFFF) >> card_size_shift);
#endif
      generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, temp2Reg, CARD_DIRTY);
      generateMemSrc1Instruction(cg, TR::InstOpCode::stb, node, TR::MemoryReference::createWithIndexReg(cg, temp1Reg, temp3Reg, 1), temp2Reg);

      generateLabelInstruction(cg, TR::InstOpCode::label, node, noChkLabel);
      }

   }

static void VMwrtbarEvaluator(TR::Node *node, TR::Register *srcReg, TR::Register *dstReg, TR::Register *dstAddrReg,
      TR::RegisterDependencyConditions *conditions, bool srcNonNull, bool needDeps, bool isCompressedRef, TR::CodeGenerator *cg, TR::Register *flagsReg = NULL)
   {
   TR::Compilation *comp = cg->comp();
   auto gcMode = TR::Compiler->om.writeBarrierType();
   bool doWrtBar = (gcMode == gc_modron_wrtbar_satb || gcMode == gc_modron_wrtbar_oldcheck || gcMode == gc_modron_wrtbar_cardmark_and_oldcheck || gcMode == gc_modron_wrtbar_always
         || comp->getOptions()->realTimeGC());
   bool doCrdMrk = ((gcMode == gc_modron_wrtbar_cardmark || gcMode == gc_modron_wrtbar_cardmark_and_oldcheck || gcMode == gc_modron_wrtbar_cardmark_incremental) && !node->isNonHeapObjectWrtBar());
   TR::LabelSymbol *label;
   TR::Register *cr0, *temp1Reg, *temp2Reg, *temp3Reg = NULL, *temp4Reg = NULL;
   uint8_t numRegs = (doWrtBar && doCrdMrk) ? 7 : 5;

   //Also need to pass in destinationAddress to jitWriteBarrierStoreMetronome
   if (comp->getOptions()->realTimeGC())
      numRegs += 3;

   if (doWrtBar || doCrdMrk)
      numRegs += 4; //two extra deps for space boundaries

   if ((!doWrtBar && !doCrdMrk) || (node->getOpCode().isWrtBar() && node->skipWrtBar()))
      {
      if (flagsReg)
         cg->stopUsingRegister(flagsReg);
      return;
      }
   if (flagsReg)
      temp1Reg = flagsReg;
   else
      temp1Reg = cg->allocateRegister();

   temp2Reg = cg->allocateRegister();
   label = generateLabelSymbol(cg);

   if (doWrtBar || doCrdMrk)
      needDeps = true;

   if (needDeps)
      {
      cr0 = cg->allocateRegister(TR_CCR);
      conditions = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(numRegs, numRegs, cg->trMemory());
      TR::addDependency(conditions, cr0, TR::RealRegister::cr0, TR_CCR, cg);
      TR::addDependency(conditions, dstReg, TR::RealRegister::gr3, TR_GPR, cg);
      TR::addDependency(conditions, temp1Reg, TR::RealRegister::gr11, TR_GPR, cg);
      if (comp->getOptions()->realTimeGC())
         {
         TR::addDependency(conditions, temp2Reg, TR::RealRegister::NoReg, TR_GPR, cg);
         conditions->getPostConditions()->getRegisterDependency(3)->setExcludeGPR0(); //3=temp2Reg
         TR::addDependency(conditions, dstAddrReg, TR::RealRegister::gr4, TR_GPR, cg);
         temp3Reg = dstAddrReg;
         }
      else
         TR::addDependency(conditions, temp2Reg, TR::RealRegister::NoReg, TR_GPR, cg);
      }
   else
      cr0 = conditions->getPostConditions()->getRegisterDependency(2)->getRegister();

   if (doWrtBar)
      {
      if (doCrdMrk)
         {
         temp3Reg = cg->allocateRegister();
         temp4Reg = cg->allocateRegister();
         if (needDeps)
            {
            TR::addDependency(conditions, temp3Reg, TR::RealRegister::NoReg, TR_GPR, cg);
            TR::addDependency(conditions, temp4Reg, TR::RealRegister::NoReg, TR_GPR, cg);
            conditions->getPostConditions()->getRegisterDependency(3)->setExcludeGPR0(); //3=temp2Reg
            }
         }
      if (needDeps)
         {
         if (comp->getOptions()->realTimeGC())
            TR::addDependency(conditions, srcReg, TR::RealRegister::gr5, TR_GPR, cg);
         else
            TR::addDependency(conditions, srcReg, TR::RealRegister::gr4, TR_GPR, cg);
         }
      if (!srcNonNull && !comp->getOptions()->realTimeGC())
         {
         generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpi, node, cr0, srcReg, NULLVALUE);
         generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, label, cr0);
         }

      VMnonNullSrcWrtBarCardCheckEvaluator(node, srcReg, dstReg, cr0, temp1Reg, temp2Reg, temp3Reg, temp4Reg, label, conditions, isCompressedRef, cg,
            flagsReg != NULL);
      generateDepLabelInstruction(cg, TR::InstOpCode::label, node, label, conditions);
      if (doCrdMrk)
         {
         cg->stopUsingRegister(temp3Reg);
         cg->stopUsingRegister(temp4Reg);
         }
      else if (comp->getOptions()->realTimeGC())
         cg->stopUsingRegister(temp3Reg);
      }
   else if (doCrdMrk)
      {
      temp3Reg = cg->allocateRegister();
      if (needDeps)
         {
         TR::addDependency(conditions, temp3Reg, TR::RealRegister::NoReg, TR_GPR, cg);
         conditions->getPostConditions()->getRegisterDependency(1)->setExcludeGPR0(); //1=dstReg
         conditions->getPostConditions()->getRegisterDependency(2)->setExcludeGPR0(); //2=temp1Reg
         }

      VMCardCheckEvaluator(node, dstReg, cr0, temp1Reg, temp2Reg, temp3Reg, conditions, cg);
      generateDepLabelInstruction(cg, TR::InstOpCode::label, node, label, conditions);
      cg->stopUsingRegister(temp3Reg);
      }
   if (needDeps)
      {
      cg->stopUsingRegister(cr0);
      }
   cg->stopUsingRegister(temp1Reg);
   cg->stopUsingRegister(temp2Reg);
   }

inline void generateLoadJ9Class(TR::Node* node, TR::Register* j9classReg, TR::Register *objReg, TR::CodeGenerator* cg)
   {
   if (TR::Compiler->om.compressObjectReferences())
      generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, j9classReg,
         TR::MemoryReference::createWithDisplacement(cg, objReg, static_cast<int32_t>(TR::Compiler->om.offsetOfObjectVftField()), 4));
   else
      generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, j9classReg,
         TR::MemoryReference::createWithDisplacement(cg, objReg, static_cast<int32_t>(TR::Compiler->om.offsetOfObjectVftField()), TR::Compiler->om.sizeofReferenceAddress()));
   TR::TreeEvaluator::generateVFTMaskInstruction(cg, node, j9classReg);
   }

void
J9::Power::TreeEvaluator::generateFillInDataBlockSequenceForUnresolvedField(TR::CodeGenerator *cg, TR::Node *node, TR::Snippet *dataSnippet, bool isWrite, TR::Register *sideEffectRegister, TR::Register *dataSnippetRegister)
   {
   TR::Compilation *comp = cg->comp();
   TR::SymbolReference *symRef = node->getSymbolReference();
   bool is64Bit = comp->target().is64Bit();
   bool isStatic = symRef->getSymbol()->getKind() == TR::Symbol::IsStatic;

   TR_RuntimeHelper helperIndex = isWrite? (isStatic ?  TR_jitResolveStaticFieldSetterDirect: TR_jitResolveFieldSetterDirect):
                                           (isStatic ?  TR_jitResolveStaticFieldDirect: TR_jitResolveFieldDirect);

   TR::Linkage *linkage = cg->getLinkage(runtimeHelperLinkage(helperIndex));
   auto linkageProperties = linkage->getProperties();
   intptr_t offsetInDataBlock = isStatic ? offsetof(J9JITWatchedStaticFieldData, fieldAddress): offsetof(J9JITWatchedInstanceFieldData, offset);


   TR::LabelSymbol* startLabel = generateLabelSymbol(cg);
   TR::LabelSymbol* endLabel = generateLabelSymbol(cg);
   TR::LabelSymbol* unresolvedLabel = generateLabelSymbol(cg);
   startLabel->setStartInternalControlFlow();
   endLabel->setEndInternalControlFlow();

   TR::Register *cpIndexReg = cg->allocateRegister();
   TR::Register *resultReg = cg->allocateRegister();
   TR::Register *scratchReg = cg->allocateRegister();
   TR::Register *jumpReg = cg->allocateRegister();

   // Check if snippet has been loaded
   if (!isStatic && !static_cast<TR::J9PPCWatchedInstanceFieldSnippet *>(dataSnippet)->isSnippetLoaded() ||
      (isStatic && !static_cast<TR::J9PPCWatchedStaticFieldSnippet *>(dataSnippet)->isSnippetLoaded()))
         loadFieldWatchSnippet(cg, node, dataSnippet, dataSnippetRegister, scratchReg, !isStatic);


   // Setup Dependencies
   // dataSnippetRegister is always used during OOL sequence.
   // Requires two argument registers: resultReg and cpIndexReg.
   // Static requires an extra arugment fieldClassReg
   // jumpReg used by trampoline
   uint8_t numOfConditions = isStatic? 5 : 4;
   TR::RegisterDependencyConditions  *deps =  new (cg->trHeapMemory()) TR::RegisterDependencyConditions(numOfConditions, numOfConditions, cg->trMemory());

   deps->addPreCondition(dataSnippetRegister, TR::RealRegister::NoReg);
   deps->addPostCondition(dataSnippetRegister, TR::RealRegister::NoReg);

   TR_PPCOutOfLineCodeSection *generateReportOOL = new (cg->trHeapMemory()) TR_PPCOutOfLineCodeSection(unresolvedLabel, endLabel, cg);
   cg->getPPCOutOfLineCodeSectionList().push_front(generateReportOOL);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, startLabel);

   // Compare J9JITWatchedInstanceFieldData.offset or J9JITWatchedStaticFieldData.fieldAddress (Depending on Instance of Static)
   // Load value from dataSnippetRegister + offsetInDataBlock then compare and branch
   TR::Register *cndReg = cg->allocateRegister(TR_CCR);
   generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, scratchReg,
                     TR::MemoryReference::createWithDisplacement(cg, dataSnippetRegister, offsetInDataBlock, TR::Compiler->om.sizeofReferenceAddress()));
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::Op_cmpi, node, cndReg, scratchReg, -1);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, unresolvedLabel, cndReg);

   generateReportOOL->swapInstructionListsWithCompilation();

   generateLabelInstruction(cg, TR::InstOpCode::label, node, unresolvedLabel);

   bool isSideEffectReg = false;
   if (isStatic)
      {
      // Fill in J9JITWatchedStaticFieldData.fieldClass
      TR::Register *fieldClassReg = NULL;

      if (isWrite)
         {
         fieldClassReg = cg->allocateRegister();
         generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, fieldClassReg,
                        TR::MemoryReference::createWithDisplacement(cg, sideEffectRegister, comp->fej9()->getOffsetOfClassFromJavaLangClassField(), TR::Compiler->om.sizeofReferenceAddress()));
         }
      else
         {
         isSideEffectReg = true;
         fieldClassReg = sideEffectRegister;
         }
      TR::MemoryReference *memRef = TR::MemoryReference::createWithDisplacement(cg, dataSnippetRegister, offsetof(J9JITWatchedStaticFieldData, fieldClass), TR::Compiler->om.sizeofReferenceAddress());

      // Store value to dataSnippetRegister + offset of fieldClass
      generateMemSrc1Instruction(cg, TR::InstOpCode::Op_st, node, memRef, fieldClassReg);
      deps->addPreCondition(fieldClassReg, TR::RealRegister::NoReg);
      deps->addPostCondition(fieldClassReg, TR::RealRegister::NoReg);
      if (!isSideEffectReg)
         cg->stopUsingRegister(fieldClassReg);
      }

   TR::ResolvedMethodSymbol *methodSymbol = node->getByteCodeInfo().getCallerIndex() == -1 ? comp->getMethodSymbol(): comp->getInlinedResolvedMethodSymbol(node->getByteCodeInfo().getCallerIndex());

   // Store cpAddressReg
   // TODO: Replace when AOT TR_ConstantPool Discontiguous support is enabled
   //loadAddressConstant(cg, node, (uintptr_t)methodSymbol->getResolvedMethod()->constantPool(), resultReg, NULL, false, TR_ConstantPool);
   loadAddressConstant(cg, comp->compileRelocatableCode(), node, reinterpret_cast<uintptr_t>(methodSymbol->getResolvedMethod()->constantPool()), resultReg);
   loadConstant(cg, node, symRef->getCPIndex(), cpIndexReg);

   // cpAddress is the first argument of VMHelper
   deps->addPreCondition(resultReg, TR::RealRegister::gr3);
   deps->addPostCondition(resultReg, TR::RealRegister::gr3);
   // cpIndexReg is the second argument
   deps->addPreCondition(cpIndexReg, TR::RealRegister::gr4);
   deps->addPostCondition(cpIndexReg, TR::RealRegister::gr4);
   // jumpReg is used in trampoline to store callee address
   deps->addPreCondition(jumpReg, TR::RealRegister::gr11);
   deps->addPostCondition(jumpReg, TR::RealRegister::gr11);

   // Generate helper address and branch
   TR::SymbolReference *helperSym = comp->getSymRefTab()->findOrCreateRuntimeHelper(helperIndex);
   TR::Instruction *call = generateDepImmSymInstruction(cg, TR::InstOpCode::bl, node, reinterpret_cast<uintptr_t>(helperSym->getMethodAddress()), deps, helperSym);
   call->PPCNeedsGCMap(linkageProperties.getPreservedRegisterMapForGC());

   /*
      * For instance field offset, the result returned by the vmhelper includes header size.
      * subtract the header size to get the offset needed by field watch helpers
   */
   if (!isStatic)
      addConstantToInteger(node, resultReg, resultReg , -TR::Compiler->om.objectHeaderSizeInBytes(), cg);

   // store result into J9JITWatchedStaticFieldData.fieldAddress / J9JITWatchedInstanceFieldData.offset
   TR::MemoryReference* dataRef = TR::MemoryReference::createWithDisplacement(cg, dataSnippetRegister, offsetInDataBlock, TR::Compiler->om.sizeofReferenceAddress());
   generateMemSrc1Instruction(cg, TR::InstOpCode::Op_st, node, dataRef, resultReg);

   generateLabelInstruction(cg, TR::InstOpCode::b, node, endLabel);

   generateReportOOL->swapInstructionListsWithCompilation();

   generateLabelInstruction(cg, TR::InstOpCode::label, node, endLabel);

   cg->stopUsingRegister(scratchReg);
   cg->stopUsingRegister(cndReg);
   cg->stopUsingRegister(cpIndexReg);
   cg->stopUsingRegister(resultReg);
   cg->stopUsingRegister(jumpReg);

   }

/*
 * Generate the reporting field access helper call with required arguments
 *
 * jitReportInstanceFieldRead
 * arg1 pointer to static data block
 * arg2 object being read
 *
 * jitReportInstanceFieldWrite
 * arg1 pointer to static data block
 * arg2 object being written to
 * arg3 pointer to value being written
 *
 * jitReportStaticFieldRead
 * arg1 pointer to static data block
 *
 * jitReportStaticFieldWrite
 * arg1 pointer to static data block
 * arg2 pointer to value being written
 *
 */
void generateReportFieldAccessOutlinedInstructions(TR::Node *node, TR::LabelSymbol *endLabel, TR::Register *dataBlockReg, bool isWrite, TR::CodeGenerator *cg, TR::Register *sideEffectRegister, TR::Register *valueReg)
   {
   TR::Compilation *comp = cg->comp();
   bool isInstanceField = node->getOpCode().isIndirect();

   TR_RuntimeHelper helperIndex = isWrite ? (isInstanceField ? TR_jitReportInstanceFieldWrite: TR_jitReportStaticFieldWrite):
                                            (isInstanceField ? TR_jitReportInstanceFieldRead: TR_jitReportStaticFieldRead);

   TR::Linkage *linkage = cg->getLinkage(runtimeHelperLinkage(helperIndex));
   auto linkageProperties = linkage->getProperties();
   TR::Register *valueReferenceReg = NULL;
   TR::Register *jumpReg = cg->allocateRegister();

   // First argument is always the data block.
   // One register used by trampoline
   uint8_t numOfConditions = 2;
   // Instance field report needs the base object
   if (isInstanceField)
      numOfConditions++;
   // Field write report needs:
   // value being written
   // the reference to the value being written
   if (isWrite)
      numOfConditions += 2;
   // Register Pair may be needed
   if(comp->target().is32Bit())
      numOfConditions++;

   TR::RegisterDependencyConditions  *deps =  new (cg->trHeapMemory())TR::RegisterDependencyConditions(numOfConditions, numOfConditions, cg->trMemory());

   /*
    * For reporting field write, reference to the valueNode is needed so we need to store
    * the value on to a stack location first and pass the stack location address as an arguement
    * to the VM helper
    */
   if (isWrite)
      {
      TR::DataType dt = node->getDataType();
      int32_t elementSize = TR::Symbol::convertTypeToSize(dt);
      TR::InstOpCode::Mnemonic storeOp = getLoadOrStoreFromDataType(cg, dt, elementSize, node->getOpCode().isUnsigned(), false);
      TR::SymbolReference *location = cg->allocateLocalTemp(dt);
      TR::MemoryReference *valueMR = TR::MemoryReference::createWithSymRef(cg, node, location, node->getSize());
      if (!valueReg->getRegisterPair())
         {
         generateMemSrc1Instruction(cg, storeOp, node, valueMR, valueReg);
         deps->addPreCondition(valueReg, TR::RealRegister::NoReg);
         deps->addPostCondition(valueReg, TR::RealRegister::NoReg);
         valueReferenceReg = cg->allocateRegister();
         }
      else
         {
         TR::MemoryReference *tempMR2 =  TR::MemoryReference::createWithMemRef(cg, node, *valueMR, 4, 4);
         generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, valueMR, valueReg->getHighOrder());
         generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, tempMR2, valueReg->getLowOrder());
         deps->addPreCondition(valueReg->getHighOrder(), TR::RealRegister::NoReg);
         deps->addPostCondition(valueReg->getHighOrder(), TR::RealRegister::NoReg);
         valueReferenceReg = valueReg->getLowOrder();
         }

      // store the stack location into a register
      generateTrg1MemInstruction(cg, TR::InstOpCode::addi2, node, valueReferenceReg, valueMR);
      }

   // First Argument - DataBlock
   deps->addPreCondition(dataBlockReg, TR::RealRegister::gr3);
   deps->addPostCondition(dataBlockReg, TR::RealRegister::gr3);

   // Second Argument
   if (isInstanceField)
      {
      deps->addPreCondition(sideEffectRegister, TR::RealRegister::gr4);
      deps->addPostCondition(sideEffectRegister, TR::RealRegister::gr4);
      }
   else if (isWrite)
      {
      deps->addPreCondition(valueReferenceReg, TR::RealRegister::gr4);
      deps->addPostCondition(valueReferenceReg, TR::RealRegister::gr4);
      }

   // Third Argument
   if (isInstanceField && isWrite)
      {
      deps->addPreCondition(valueReferenceReg, TR::RealRegister::gr5);
      deps->addPostCondition(valueReferenceReg, TR::RealRegister::gr5);
      }

   // Register ued by trampoline to store callee address
   deps->addPreCondition(jumpReg, TR::RealRegister::gr11);
   deps->addPostCondition(jumpReg, TR::RealRegister::gr11);

   // Generate branch instruction to jump into helper
   TR::SymbolReference *helperSym = comp->getSymRefTab()->findOrCreateRuntimeHelper(helperIndex);
   TR::Instruction *call = generateDepImmSymInstruction(cg, TR::InstOpCode::bl, node, reinterpret_cast<uintptr_t>(helperSym->getMethodAddress()), deps, helperSym);
   call->PPCNeedsGCMap(linkageProperties.getPreservedRegisterMapForGC());

   generateLabelInstruction(cg, TR::InstOpCode::b, node, endLabel);

   cg->stopUsingRegister(valueReferenceReg);
   cg->stopUsingRegister(jumpReg);

   }

void loadFieldWatchSnippet(TR::CodeGenerator *cg, TR::Node *node, TR::Snippet *dataSnippet, TR::Register *snippetReg, TR::Register *scratchReg, bool isInstanceField)
   {
   TR::Compilation *comp = cg->comp();
   int32_t beginIndex = PTOC_FULL_INDEX;

   cg->fixedLoadLabelAddressIntoReg(node, snippetReg, dataSnippet->getSnippetLabel());

   // Loaded Snippet
   if (!isInstanceField)
      {
      static_cast<TR::J9PPCWatchedStaticFieldSnippet *>(dataSnippet)->setLoadSnippet();
      }
   else
      {
      static_cast<TR::J9PPCWatchedInstanceFieldSnippet *>(dataSnippet)->setLoadSnippet();
      }
   }

TR::Snippet *
J9::Power::TreeEvaluator::getFieldWatchInstanceSnippet(TR::CodeGenerator *cg, TR::Node *node, J9Method *m, UDATA loc, UDATA os)
   {
   return new (cg->trHeapMemory()) TR::J9PPCWatchedInstanceFieldSnippet(cg, node, m, loc, os);
   }

TR::Snippet *
J9::Power::TreeEvaluator::getFieldWatchStaticSnippet(TR::CodeGenerator *cg, TR::Node *node, J9Method *m, UDATA loc, void *fieldAddress, J9Class *fieldClass)
   {
   return new (cg->trHeapMemory()) TR::J9PPCWatchedStaticFieldSnippet(cg, node, m, loc, fieldAddress, fieldClass);
   }

void
J9::Power::TreeEvaluator::generateTestAndReportFieldWatchInstructions(TR::CodeGenerator *cg, TR::Node *node, TR::Snippet *dataSnippet, bool isWrite, TR::Register *sideEffectRegister, TR::Register *valueReg, TR::Register *dataSnippetRegister)
   {
   bool isInstanceField = node->getOpCode().isIndirect();
   TR_J9VMBase *fej9 = (TR_J9VMBase *)(cg->fe());

   TR::Register *scratchReg = cg->allocateRegister();

   // Check if snippet has been loaded
   if (isInstanceField && !static_cast<TR::J9PPCWatchedInstanceFieldSnippet *>(dataSnippet)->isSnippetLoaded() ||
      (!isInstanceField && !static_cast<TR::J9PPCWatchedStaticFieldSnippet *>(dataSnippet)->isSnippetLoaded()))
      loadFieldWatchSnippet(cg, node, dataSnippet, dataSnippetRegister, scratchReg, isInstanceField);

   TR::LabelSymbol* startLabel = generateLabelSymbol(cg);
   TR::LabelSymbol* endLabel = generateLabelSymbol(cg);
   TR::LabelSymbol* fieldReportLabel = generateLabelSymbol(cg);
   startLabel->setStartInternalControlFlow();
   endLabel->setEndInternalControlFlow();

   generateLabelInstruction(cg, TR::InstOpCode::label, node, startLabel);

   TR_PPCOutOfLineCodeSection *generateReportOOL = new (cg->trHeapMemory()) TR_PPCOutOfLineCodeSection(fieldReportLabel, endLabel, cg);
   cg->getPPCOutOfLineCodeSectionList().push_front(generateReportOOL);

   TR::Register *fieldClassReg = NULL;
   bool isSideEffectReg = false;
   // Load fieldClass
   if (isInstanceField)
      {
      fieldClassReg = cg->allocateRegister();
      generateLoadJ9Class(node, fieldClassReg, sideEffectRegister, cg);
      }
   else if (!(node->getSymbolReference()->isUnresolved()))
      {
      fieldClassReg = cg->allocateRegister();
      // During Non-AOT (JIT and JITServer) compilation the fieldClass has been populated inside the dataSnippet during compilation.
      // During AOT compilation the fieldClass must be loaded from the snippet. The fieldClass in an AOT body is invalid.
      if (cg->needClassAndMethodPointerRelocations())
         {
         // Load FieldClass from snippet
         generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, fieldClassReg,
                           TR::MemoryReference::createWithDisplacement(cg, dataSnippetRegister, offsetof(J9JITWatchedStaticFieldData, fieldClass),
                           TR::Compiler->om.sizeofReferenceAddress()));
         }
      else
         {
         J9Class * fieldClass = static_cast<TR::J9PPCWatchedStaticFieldSnippet *>(dataSnippet)->getFieldClass();
         loadAddressConstant(cg, false, node, reinterpret_cast<uintptr_t>(fieldClass), fieldClassReg);
         }
      }
   else
      {
      // Unresolved
      if (isWrite)
         {
         fieldClassReg = cg->allocateRegister();
         generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, fieldClassReg,
               TR::MemoryReference::createWithDisplacement(cg, sideEffectRegister, fej9->getOffsetOfClassFromJavaLangClassField(), TR::Compiler->om.sizeofReferenceAddress()));
         }
      else
         {
         isSideEffectReg = true;
         fieldClassReg = sideEffectRegister;
         }
      }

   TR::MemoryReference *classFlagsMemRef = TR::MemoryReference::createWithDisplacement(cg, fieldClassReg, static_cast<uintptr_t>(fej9->getOffsetOfClassFlags()), 4);

   TR::Register *cndReg = cg->allocateRegister(TR_CCR);
   generateTrg1MemInstruction(cg,TR::InstOpCode::lwz, node, scratchReg, classFlagsMemRef);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, scratchReg, scratchReg, cndReg, J9ClassHasWatchedFields);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, fieldReportLabel, cndReg);

   generateReportOOL->swapInstructionListsWithCompilation();

   generateLabelInstruction(cg, TR::InstOpCode::label, node, fieldReportLabel);
   generateReportFieldAccessOutlinedInstructions(node, endLabel, dataSnippetRegister, isWrite, cg, sideEffectRegister, valueReg);

   generateReportOOL->swapInstructionListsWithCompilation();

   generateLabelInstruction(cg, TR::InstOpCode::label, node, endLabel);

   cg->stopUsingRegister(cndReg);
   cg->stopUsingRegister(scratchReg);
   if (!isSideEffectReg)
      cg->stopUsingRegister(fieldClassReg);
   cg->stopUsingRegister(valueReg);

   cg->machine()->setLinkRegisterKilled(true);

   }

TR::Register *J9::Power::TreeEvaluator::awrtbarEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (comp->fe());
   TR::Register *valueReg = cg->evaluate(node->getFirstChild());
   TR::Register *sideEffectRegister = cg->evaluate(node->getSecondChild());
   if (comp->getOption(TR_EnableFieldWatch) && !node->getSymbolReference()->getSymbol()->isShadow())
      {
      TR::TreeEvaluator::rdWrtbarHelperForFieldWatch(node, cg, sideEffectRegister, valueReg);
      }

   if (comp->isOptServer() && !comp->compileRelocatableCode() && !comp->getOptions()->realTimeGC())
      {
      if (!comp->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P8))
         {
         static bool disableOutlinedWrtbar = feGetEnv("TR_ppcDisableOutlinedWriteBarrier") != NULL;
         if (!disableOutlinedWrtbar)
            {
            return outlinedHelperWrtbarEvaluator(node, cg);
            }
         }
      else
         {
         static bool enableOutlinedWrtbar = feGetEnv("TR_ppcEnableOutlinedWriteBarrier") != NULL;
         if (enableOutlinedWrtbar)
            {
            return outlinedHelperWrtbarEvaluator(node, cg);
            }
         }
      }

   TR::Register *destinationRegister = cg->evaluate(node->getSecondChild());
   TR::Register *flagsReg = NULL;
   TR::Node *firstChild = node->getFirstChild();
   TR::Register *sourceRegister;
   bool killSource = false;

   if (firstChild->getReferenceCount() > 1 && firstChild->getRegister() != NULL)
      {
      if (!firstChild->getRegister()->containsInternalPointer())
         sourceRegister = cg->allocateCollectedReferenceRegister();
      else
         {
         sourceRegister = cg->allocateRegister();
         sourceRegister->setPinningArrayPointer(firstChild->getRegister()->getPinningArrayPointer());
         sourceRegister->setContainsInternalPointer();
         }
      generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, sourceRegister, firstChild->getRegister());
      killSource = true;
      }
   else
      sourceRegister = cg->evaluate(firstChild);

   if (!node->skipWrtBar() && !node->hasUnresolvedSymbolReference()
         && (TR::Compiler->om.writeBarrierType() == gc_modron_wrtbar_oldcheck || TR::Compiler->om.writeBarrierType() == gc_modron_wrtbar_cardmark_and_oldcheck))
      {
      flagsReg = cg->allocateRegister();
      generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, flagsReg, TR::MemoryReference::createWithDisplacement(cg, destinationRegister, getOffsetOfJ9ObjectFlags(), 4));
      }

   // RealTimeGC write barriers occur BEFORE the store
   if (comp->getOptions()->realTimeGC())
      {
      TR::Register *destinationAddressRegister = cg->allocateRegister();

      TR::LoadStoreHandler::generateComputeAddressSequence(cg, destinationAddressRegister, node);
      VMwrtbarEvaluator(node, sourceRegister, destinationRegister, destinationAddressRegister, NULL, firstChild->isNonNull(), true, false, cg);
      TR::LoadStoreHandler::generateStoreAddressSequence(cg, sourceRegister, node, destinationAddressRegister, TR::InstOpCode::Op_st, TR::Compiler->om.sizeofReferenceAddress());

      cg->stopUsingRegister(destinationAddressRegister);
      }
   else
      {
      TR::LoadStoreHandler::generateStoreNodeSequence(cg, sourceRegister, node, TR::InstOpCode::Op_st, TR::Compiler->om.sizeofReferenceAddress());
      VMwrtbarEvaluator(node, sourceRegister, destinationRegister, NULL, NULL, firstChild->isNonNull(), true, false, cg, flagsReg);
      }

   if (killSource)
      cg->stopUsingRegister(sourceRegister);

   cg->decReferenceCount(node->getFirstChild());
   cg->decReferenceCount(node->getSecondChild());

   return NULL;
   }

TR::Register *J9::Power::TreeEvaluator::awrtbariEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());

   TR::Node *valueNode = NULL;
   TR::TreeEvaluator::getIndirectWrtbarValueNode(cg, node, valueNode, false);
   TR::Register *valueReg = cg->evaluate(valueNode);
   TR::Register *sideEffectRegister = cg->evaluate(node->getThirdChild());

   TR::Register *destinationRegister = cg->evaluate(node->getChild(2));
   TR::Node *secondChild = node->getSecondChild();
   TR::Register *sourceRegister;
   TR::Register *flagsReg = NULL;
   bool killSource = false;

   bool usingCompressedPointers = false;
   bool bumpedRefCount = false;

   if (comp->getOption(TR_EnableFieldWatch) && !node->getSymbolReference()->getSymbol()->isArrayShadowSymbol())
      {
      // The Third child (sideEffectNode) and valueReg's node is also used by the store evaluator below.
      // The store evaluator will also evaluate+decrement it. In order to avoid double
      // decrementing the node we skip doing it here and let the store evaluator do it.
      TR::TreeEvaluator::rdWrtbarHelperForFieldWatch(node, cg, sideEffectRegister, valueReg);
      }

   if (comp->useCompressedPointers() && (node->getSymbolReference()->getSymbol()->getDataType() == TR::Address) && (node->getSecondChild()->getDataType() != TR::Address))
      {
      usingCompressedPointers = true;
      while (secondChild->getNumChildren() && secondChild->getOpCodeValue() != TR::a2l)
         secondChild = secondChild->getFirstChild();
      if (secondChild->getNumChildren())
         secondChild = secondChild->getFirstChild();
      }

   int32_t sizeofMR = TR::Compiler->om.sizeofReferenceAddress();
   if (usingCompressedPointers)
      sizeofMR = TR::Compiler->om.sizeofReferenceField();

   TR::Register *compressedReg;
   if (secondChild->getReferenceCount() > 1 && secondChild->getRegister() != NULL)
      {
      if (!secondChild->getRegister()->containsInternalPointer())
         sourceRegister = cg->allocateCollectedReferenceRegister();
      else
         {
         sourceRegister = cg->allocateRegister();
         sourceRegister->setPinningArrayPointer(secondChild->getRegister()->getPinningArrayPointer());
         sourceRegister->setContainsInternalPointer();
         }
      generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, sourceRegister, secondChild->getRegister());
      killSource = true;
      compressedReg = sourceRegister;
      if (usingCompressedPointers)
         compressedReg = cg->evaluate(node->getSecondChild());
      }
   else
      {
      sourceRegister = cg->evaluate(secondChild);
      compressedReg = sourceRegister;
      if (usingCompressedPointers)
         compressedReg = cg->evaluate(node->getSecondChild());
      }

   if (!node->skipWrtBar() && !node->hasUnresolvedSymbolReference()
         && (TR::Compiler->om.writeBarrierType() == gc_modron_wrtbar_oldcheck || TR::Compiler->om.writeBarrierType() == gc_modron_wrtbar_cardmark_and_oldcheck))
      {
      flagsReg = cg->allocateRegister();
      generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, flagsReg, TR::MemoryReference::createWithDisplacement(cg, destinationRegister, getOffsetOfJ9ObjectFlags(), 4));
      }

   TR::InstOpCode::Mnemonic storeOp = usingCompressedPointers ? TR::InstOpCode::stw : TR::InstOpCode::Op_st;
   TR::Register *storeRegister = usingCompressedPointers ? compressedReg : sourceRegister;

   // RealTimeGC write barriers occur BEFORE the store
   if (comp->getOptions()->realTimeGC())
      {
      TR::Register *destinationAddressRegister = cg->allocateRegister();

      TR::LoadStoreHandler::generateComputeAddressSequence(cg, destinationAddressRegister, node);
      VMwrtbarEvaluator(node, sourceRegister, destinationRegister, destinationAddressRegister, NULL, secondChild->isNonNull(), true, usingCompressedPointers, cg);
      TR::LoadStoreHandler::generateStoreAddressSequence(cg, storeRegister, node, destinationAddressRegister, storeOp, sizeofMR);

      cg->stopUsingRegister(destinationAddressRegister);
      }
   else
      {
      TR::LoadStoreHandler::generateStoreNodeSequence(cg, storeRegister, node, storeOp, sizeofMR);
      VMwrtbarEvaluator(node, sourceRegister, destinationRegister, NULL, NULL, secondChild->isNonNull(), true, usingCompressedPointers, cg, flagsReg);
      }

   if (killSource)
      cg->stopUsingRegister(sourceRegister);

   cg->decReferenceCount(node->getSecondChild());
   cg->decReferenceCount(node->getChild(2));

   if (comp->useCompressedPointers())
      node->setStoreAlreadyEvaluated(true);

   return NULL;
   }

TR::Register *iGenerateSoftwareReadBarrier(TR::Node *node, TR::CodeGenerator *cg)
   {
#ifndef OMR_GC_CONCURRENT_SCAVENGER
   TR_ASSERT_FATAL(false, "Concurrent Scavenger not supported.");
#else
   TR::Compilation *comp = cg->comp();

   TR::Register *objReg = cg->allocateRegister();
   TR::Register *locationReg = cg->allocateRegister();
   TR::Register *evacuateReg = cg->allocateRegister();
   TR::Register *r3Reg = cg->allocateRegister();
   TR::Register *r11Reg = cg->allocateRegister();
   TR::Register *metaReg = cg->getMethodMetaDataRegister();
   TR::Register *condReg = cg->allocateRegister(TR_CCR);

   TR::LabelSymbol *startLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *endLabel = generateLabelSymbol(cg);
   startLabel->setStartInternalControlFlow();
   endLabel->setEndInternalControlFlow();

   TR::RegisterDependencyConditions *deps = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, 7, cg->trMemory());
   deps->addPostCondition(objReg, TR::RealRegister::NoReg);
   deps->addPostCondition(locationReg, TR::RealRegister::gr4); //TR_softwareReadBarrier helper needs this in gr4.
   deps->addPostCondition(evacuateReg, TR::RealRegister::NoReg);
   deps->addPostCondition(r3Reg, TR::RealRegister::gr3);
   deps->addPostCondition(r11Reg, TR::RealRegister::gr11);
   deps->addPostCondition(metaReg, TR::RealRegister::NoReg);
   deps->addPostCondition(condReg, TR::RealRegister::NoReg);

   if (node->getSymbolReference()->getSymbol()->isInternalPointer())
      {
      objReg->setPinningArrayPointer(node->getSymbolReference()->getSymbol()->castToInternalPointerAutoSymbol()->getPinningArrayPointer());
      objReg->setContainsInternalPointer();
      }

   node->setRegister(objReg);

   TR::LoadStoreHandler::generateComputeAddressSequence(cg, locationReg, node);

   generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, objReg, TR::MemoryReference::createWithDisplacement(cg, locationReg, 0, 4));

   generateLabelInstruction(cg, TR::InstOpCode::label, node, startLabel);
   generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, evacuateReg,
         TR::MemoryReference::createWithDisplacement(cg, metaReg, comp->fej9()->thisThreadGetEvacuateBaseAddressOffset(), 4));
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmpl4, node, condReg, objReg, evacuateReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, endLabel, condReg);

   generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, evacuateReg,
         TR::MemoryReference::createWithDisplacement(cg, metaReg, comp->fej9()->thisThreadGetEvacuateTopAddressOffset(), 4));
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmpl4, node, condReg, objReg, evacuateReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bgt, node, endLabel, condReg);

   // TR_softwareReadBarrier helper expects the vmThread in r3.
   generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, r3Reg, metaReg);

   TR::SymbolReference *helperSym = comp->getSymRefTab()->findOrCreateRuntimeHelper(TR_softwareReadBarrier);
   generateDepImmSymInstruction(cg, TR::InstOpCode::bl, node, (uintptr_t)helperSym->getMethodAddress(), deps, helperSym);

   generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, objReg, TR::MemoryReference::createWithDisplacement(cg, locationReg, 0, 4));

   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, endLabel, deps);

   // TODO: Allow this to be patched or skipped at runtime for unresolved symrefs
   if (node->getSymbol()->isAtLeastOrStrongerThanAcquireRelease() && comp->target().isSMP())
      {
      generateInstruction(cg, comp->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P7) ? TR::InstOpCode::lwsync : TR::InstOpCode::isync, node);
      }

   cg->insertPrefetchIfNecessary(node, objReg);

   cg->stopUsingRegister(evacuateReg);
   cg->stopUsingRegister(locationReg);
   cg->stopUsingRegister(r3Reg);
   cg->stopUsingRegister(r11Reg);
   cg->stopUsingRegister(condReg);

   cg->machine()->setLinkRegisterKilled(true);

   return objReg;
#endif
   }

TR::Register *aGenerateSoftwareReadBarrier(TR::Node *node, TR::CodeGenerator *cg)
   {
#ifndef OMR_GC_CONCURRENT_SCAVENGER
   TR_ASSERT_FATAL(false, "Concurrent Scavenger not supported.");
#else
   TR::Compilation *comp = cg->comp();

   TR::Register *tempReg;
   TR::Register *locationReg = cg->allocateRegister();
   TR::Register *evacuateReg = cg->allocateRegister();
   TR::Register *r3Reg = cg->allocateRegister();
   TR::Register *r11Reg = cg->allocateRegister();
   TR::Register *metaReg = cg->getMethodMetaDataRegister();
   TR::Register *condReg = cg->allocateRegister(TR_CCR);

   if (!node->getSymbolReference()->getSymbol()->isInternalPointer())
      {
      if (node->getSymbolReference()->getSymbol()->isNotCollected())
         tempReg = cg->allocateRegister();
      else
         tempReg = cg->allocateCollectedReferenceRegister();
      }
   else
      {
      tempReg = cg->allocateRegister();
      tempReg->setPinningArrayPointer(node->getSymbolReference()->getSymbol()->castToInternalPointerAutoSymbol()->getPinningArrayPointer());
      tempReg->setContainsInternalPointer();
      }

   TR::LabelSymbol *startLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *endLabel = generateLabelSymbol(cg);
   startLabel->setStartInternalControlFlow();
   endLabel->setEndInternalControlFlow();

   TR::RegisterDependencyConditions *deps = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, 7, cg->trMemory());
   deps->addPostCondition(tempReg, TR::RealRegister::NoReg);
   deps->addPostCondition(locationReg, TR::RealRegister::gr4); //TR_softwareReadBarrier helper needs this in gr4.
   deps->addPostCondition(evacuateReg, TR::RealRegister::NoReg);
   deps->addPostCondition(r3Reg, TR::RealRegister::gr3);
   deps->addPostCondition(r11Reg, TR::RealRegister::gr11);
   deps->addPostCondition(metaReg, TR::RealRegister::NoReg);
   deps->addPostCondition(condReg, TR::RealRegister::NoReg);

   node->setRegister(tempReg);

   TR::LoadStoreHandler::generateComputeAddressSequence(cg, locationReg, node);

   generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, tempReg, TR::MemoryReference::createWithDisplacement(cg, locationReg, 0, TR::Compiler->om.sizeofReferenceAddress()));

   if (node->getSymbolReference() == comp->getSymRefTab()->findVftSymbolRef())
      TR::TreeEvaluator::generateVFTMaskInstruction(cg, node, tempReg);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, startLabel);

   generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, evacuateReg,
         TR::MemoryReference::createWithDisplacement(cg, metaReg, comp->fej9()->thisThreadGetEvacuateBaseAddressOffset(), TR::Compiler->om.sizeofReferenceAddress()));
   generateTrg1Src2Instruction(cg, TR::InstOpCode::Op_cmpl, node, condReg, tempReg, evacuateReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, endLabel, condReg);

   generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, evacuateReg,
         TR::MemoryReference::createWithDisplacement(cg, metaReg, comp->fej9()->thisThreadGetEvacuateTopAddressOffset(), TR::Compiler->om.sizeofReferenceAddress()));
   generateTrg1Src2Instruction(cg, TR::InstOpCode::Op_cmpl, node, condReg, tempReg, evacuateReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bgt, node, endLabel, condReg);

   // TR_softwareReadBarrier helper expects the vmThread in r3.
   generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, r3Reg, metaReg);

   TR::SymbolReference *helperSym = comp->getSymRefTab()->findOrCreateRuntimeHelper(TR_softwareReadBarrier);
   generateDepImmSymInstruction(cg, TR::InstOpCode::bl, node, (uintptr_t)helperSym->getMethodAddress(), deps, helperSym);

   generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, tempReg, TR::MemoryReference::createWithDisplacement(cg, locationReg, 0, TR::Compiler->om.sizeofReferenceAddress()));

   if (node->getSymbolReference() == comp->getSymRefTab()->findVftSymbolRef())
      TR::TreeEvaluator::generateVFTMaskInstruction(cg, node, tempReg);

   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, endLabel, deps);

   // TODO: Allow this to be patched or skipped at runtime for unresolved symrefs
   if (node->getSymbol()->isAtLeastOrStrongerThanAcquireRelease() && comp->target().isSMP())
      {
      generateInstruction(cg, comp->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P7) ? TR::InstOpCode::lwsync : TR::InstOpCode::isync, node);
      }

   cg->stopUsingRegister(evacuateReg);
   cg->stopUsingRegister(locationReg);
   cg->stopUsingRegister(r3Reg);
   cg->stopUsingRegister(r11Reg);
   cg->stopUsingRegister(condReg);

   cg->machine()->setLinkRegisterKilled(true);

   return tempReg;
#endif
   }

TR::Register *J9::Power::TreeEvaluator::fwrtbarEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   // For rdbar and wrtbar nodes we first evaluate the children we need to
   // handle the side effects. Then we delegate the evaluation of the remaining
   // children and the load/store operation to the appropriate load/store evaluator.
   TR::Node *sideEffectNode = node->getSecondChild();
   TR::Register *valueReg = cg->evaluate(node->getFirstChild());
   TR::Register *sideEffectRegister = cg->evaluate(sideEffectNode);
   if (cg->comp()->getOption(TR_EnableFieldWatch))
      {
      TR::TreeEvaluator::rdWrtbarHelperForFieldWatch(node, cg, sideEffectRegister, valueReg);
      }
   // The Value Node, or the second child is not decremented here. The store evaluator also uses it, and decrements it.
   cg->decReferenceCount(sideEffectNode);
   return TR::TreeEvaluator::fstoreEvaluator(node, cg);
   }

TR::Register *J9::Power::TreeEvaluator::fwrtbariEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   // For rdbar and wrtbar nodes we first evaluate the children we need to
   // handle the side effects. Then we delegate the evaluation of the remaining
   // children and the load/store operation to the appropriate load/store evaluator.
   TR::Node *sideEffectNode = node->getThirdChild();
   TR::Register *valueReg = cg->evaluate(node->getSecondChild());
   TR::Register *sideEffectRegister = cg->evaluate(sideEffectNode);
   if (cg->comp()->getOption(TR_EnableFieldWatch))
      {
      TR::TreeEvaluator::rdWrtbarHelperForFieldWatch(node, cg, sideEffectRegister, valueReg);
      }
   // The Value Node, or the second child is not decremented here. The store evaluator also uses it, and decrements it.
   cg->decReferenceCount(sideEffectNode);
   return TR::TreeEvaluator::fstoreEvaluator(node, cg);
   }

TR::Register *J9::Power::TreeEvaluator::dwrtbarEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   // For rdbar and wrtbar nodes we first evaluate the children we need to
   // handle the side effects. Then we delegate the evaluation of the remaining
   // children and the load/store operation to the appropriate load/store evaluator.
   TR::Node *sideEffectNode = node->getSecondChild();
   TR::Register *valueReg = cg->evaluate(node->getFirstChild());
   TR::Register *sideEffectRegister = cg->evaluate(node->getSecondChild());
   if (cg->comp()->getOption(TR_EnableFieldWatch))
      {
      TR::TreeEvaluator::rdWrtbarHelperForFieldWatch(node, cg, sideEffectRegister, valueReg);
      }
   // The Value Node, or the second child is not decremented here. The store evaluator also uses it, and decrements it.
   cg->decReferenceCount(sideEffectNode);
   return TR::TreeEvaluator::dstoreEvaluator(node, cg);
   }

TR::Register *J9::Power::TreeEvaluator::dwrtbariEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   // For rdbar and wrtbar nodes we first evaluate the children we need to
   // handle the side effects. Then we delegate the evaluation of the remaining
   // children and the load/store operation to the appropriate load/store evaluator.
   TR::Node *sideEffectNode = node->getThirdChild();
   TR::Register *valueReg = cg->evaluate(node->getSecondChild());
   TR::Register *sideEffectRegister = cg->evaluate(node->getThirdChild());
   if (cg->comp()->getOption(TR_EnableFieldWatch))
      {
      TR::TreeEvaluator::rdWrtbarHelperForFieldWatch(node, cg, sideEffectRegister, valueReg);
      }
   // The Value Node, or the second child is not decremented here. The store evaluator also uses it, and decrements it.
   cg->decReferenceCount(sideEffectNode);
   return TR::TreeEvaluator::dstoreEvaluator(node, cg);
   }

TR::Register *J9::Power::TreeEvaluator::irdbarEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   // For rdbar and wrtbar nodes we first evaluate the children we need to
   // handle the side effects. Then we delegate the evaluation of the remaining
   // children and the load/store operation to the appropriate load/store evaluator.
   TR::Node *sideEffectNode = node->getFirstChild();
   TR::Register * sideEffectRegister = cg->evaluate(sideEffectNode);
   if (cg->comp()->getOption(TR_EnableFieldWatch))
      {
      TR::TreeEvaluator::rdWrtbarHelperForFieldWatch(node, cg, sideEffectRegister, NULL);
      }
   cg->decReferenceCount(sideEffectNode);
   return TR::TreeEvaluator::iloadEvaluator(node, cg);
   }

TR::Register *J9::Power::TreeEvaluator::irdbariEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   // For rdbar and wrtbar nodes we first evaluate the children we need to
   // handle the side effects. Then we delegate the evaluation of the remaining
   // children and the load/store operation to the appropriate load/store evaluator.
   TR::Node *sideEffectNode = node->getFirstChild();
   TR::Register *sideEffectRegister = cg->evaluate(sideEffectNode);
   if (cg->comp()->getOption(TR_EnableFieldWatch))
      {
      TR::TreeEvaluator::rdWrtbarHelperForFieldWatch(node, cg, sideEffectRegister, NULL);
      }

   // Note: For indirect rdbar nodes, the first child (sideEffectNode) is also used by the
   // load evaluator. The load evaluator will also evaluate+decrement it. In order to avoid double
   // decrementing the node we skip doing it here and let the load evaluator do it.
   if (TR::Compiler->om.readBarrierType() != gc_modron_readbar_none &&
       cg->comp()->useCompressedPointers() &&
       (node->getOpCode().hasSymbolReference() &&
        node->getSymbolReference()->getSymbol()->getDataType() == TR::Address))
      {
      return iGenerateSoftwareReadBarrier(node, cg);
      }
   else
      return TR::TreeEvaluator::iloadEvaluator(node, cg);
   }

TR::Register *J9::Power::TreeEvaluator::ardbarEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   // For rdbar and wrtbar nodes we first evaluate the children we need to
   // handle the side effects. Then we delegate the evaluation of the remaining
   // children and the load/store operation to the appropriate load/store evaluator.
   TR::Node *sideEffectNode = node->getFirstChild();
   TR::Register *sideEffectRegister = cg->evaluate(sideEffectNode);
   if (cg->comp()->getOption(TR_EnableFieldWatch))
      {
      TR::TreeEvaluator::rdWrtbarHelperForFieldWatch(node, cg, sideEffectRegister, NULL);
      }
   cg->decReferenceCount(sideEffectNode);
   return TR::TreeEvaluator::aloadEvaluator(node, cg);
   }

TR::Register *J9::Power::TreeEvaluator::ardbariEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   // For rdbar and wrtbar nodes we first evaluate the children we need to
   // handle the side effects. Then we delegate the evaluation of the remaining
   // children and the load/store operation to the appropriate load/store evaluator.
   TR::Register *sideEffectRegister = cg->evaluate(node->getFirstChild());
   if (cg->comp()->getOption(TR_EnableFieldWatch))
      {
      TR::TreeEvaluator::rdWrtbarHelperForFieldWatch(node, cg, sideEffectRegister, NULL);
      }
   // Note: For indirect rdbar nodes, the first child (sideEffectNode) is also used by the
   // load evaluator. The load evaluator will also evaluate+decrement it. In order to avoid double
   // decrementing the node we skip doing it here and let the load evaluator do it.
   if (TR::Compiler->om.readBarrierType() == gc_modron_readbar_none)
      return TR::TreeEvaluator::aloadEvaluator(node, cg);
   else
      return aGenerateSoftwareReadBarrier(node, cg);
   }

TR::Register *J9::Power::TreeEvaluator::monentEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   return TR::TreeEvaluator::VMmonentEvaluator(node, cg);
   }

TR::Register *J9::Power::TreeEvaluator::monexitEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   return TR::TreeEvaluator::VMmonexitEvaluator(node, cg);
   }

TR::Register *J9::Power::TreeEvaluator::asynccheckEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   // The child contains an inline test. If it succeeds, the helper is called
   // or (with TR_ASYNC_CHECK_TRAPS) trap handler is invoked.
   // The address of the helper is contained as an int in this node.
   //
   TR::Compilation *comp = cg->comp();
   TR::Node *testNode = node->getFirstChild();
   TR::Node *firstChild = testNode->getFirstChild();
   TR::Register *src1Reg = cg->evaluate(firstChild);
   TR::Node *secondChild = testNode->getSecondChild();

   TR_ASSERT(testNode->getOpCodeValue() == (comp->target().is64Bit() ? TR::lcmpeq : TR::icmpeq), "asynccheck bad format");
   TR_ASSERT(secondChild->getOpCode().isLoadConst() && secondChild->getRegister() == NULL, "asynccheck bad format");

   TR::Instruction *gcPoint;

#if defined(TR_ASYNC_CHECK_TRAPS)
   if (cg->getHasResumableTrapHandler())
      {
      if (comp->target().is64Bit())
         {
         TR_ASSERT(secondChild->getLongInt() == -1L, "asynccheck bad format");
         gcPoint = generateSrc1Instruction(cg, TR::InstOpCode::tdeqi, node, src1Reg, secondChild->getLongInt());
         }
      else
         {
         TR_ASSERT(secondChild->getInt() == -1, "asynccheck bad format");
         gcPoint = generateSrc1Instruction(cg, TR::InstOpCode::tweqi, node, src1Reg, secondChild->getInt());
         }
      cg->setCanExceptByTrap();
      }
   else
#endif
      {
      TR::Register *condReg = cg->allocateRegister(TR_CCR);
      if (comp->target().is64Bit())
         {
         TR_ASSERT(secondChild->getLongInt() == -1L, "asynccheck bad format");
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi8, node, condReg, src1Reg, secondChild->getLongInt());
         }
      else
         {
         TR_ASSERT(secondChild->getInt() == -1, "asynccheck bad format");
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, condReg, src1Reg, secondChild->getInt());
         }

      TR::LabelSymbol *snippetLabel;
      TR::Register *jumpRegister = cg->allocateRegister();
      TR::RegisterDependencyConditions *dependencies = createConditionsAndPopulateVSXDeps(cg, 2);

      snippetLabel = cg->lookUpSnippet(TR::Snippet::IsHelperCall, node->getSymbolReference());
      if (snippetLabel == NULL)
         {
         snippetLabel = generateLabelSymbol(cg);
         cg->addSnippet(new (cg->trHeapMemory()) TR::PPCHelperCallSnippet(cg, node, snippetLabel, node->getSymbolReference()));
         }
      TR::addDependency(dependencies, jumpRegister, TR::RealRegister::gr11, TR_GPR, cg);
      TR::addDependency(dependencies, condReg, TR::RealRegister::cr7, TR_CCR, cg);
      gcPoint = generateDepConditionalBranchInstruction(cg, TR::InstOpCode::beql, PPCOpProp_BranchUnlikely, node, snippetLabel, condReg, dependencies);
      gcPoint->setAsyncBranch();
      cg->machine()->setLinkRegisterKilled(true);

      dependencies->stopUsingDepRegs(cg);
      }

   gcPoint->PPCNeedsGCMap(0xFFFFFFFF);
   cg->decReferenceCount(firstChild);
   cg->decReferenceCount(secondChild);
   cg->decReferenceCount(testNode);
   return NULL;
   }

TR::Register *J9::Power::TreeEvaluator::instanceofEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   return VMinstanceOfEvaluator(node, cg);
   }

TR::Register *J9::Power::TreeEvaluator::checkcastEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   return TR::TreeEvaluator::VMcheckcastEvaluator(node, cg);
   }

TR::Register *J9::Power::TreeEvaluator::checkcastAndNULLCHKEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   return checkcastEvaluator(node, cg);
   }

TR::Register *J9::Power::TreeEvaluator::newObjectEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR::Compilation* comp = cg->comp();
   if (comp->suppressAllocationInlining() ||
       TR::TreeEvaluator::requireHelperCallValueTypeAllocation(node, cg))
      {
      TR::ILOpCodes opCode = node->getOpCodeValue();
      TR::Node::recreate(node, TR::acall);
      TR::Register *targetRegister = directCallEvaluator(node, cg);
      TR::Node::recreate(node, opCode);
      return targetRegister;
      }
   else
      return TR::TreeEvaluator::VMnewEvaluator(node, cg);
   }

TR::Register *J9::Power::TreeEvaluator::newArrayEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   if (cg->comp()->suppressAllocationInlining())
      {
      TR::ILOpCodes opCode = node->getOpCodeValue();
      TR::Node::recreate(node, TR::acall);
      TR::Register *targetRegister = directCallEvaluator(node, cg);
      TR::Node::recreate(node, opCode);
      return targetRegister;
      }
   else
      return TR::TreeEvaluator::VMnewEvaluator(node, cg);
   }

TR::Register *J9::Power::TreeEvaluator::anewArrayEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   if (cg->comp()->suppressAllocationInlining())
      {
      TR::ILOpCodes opCode = node->getOpCodeValue();
      TR::Node::recreate(node, TR::acall);
      TR::Register *targetRegister = directCallEvaluator(node, cg);
      TR::Node::recreate(node, opCode);
      return targetRegister;
      }
   else
      return TR::TreeEvaluator::VMnewEvaluator(node, cg);
   }

static TR::Register *generateMultianewArrayWithInlineAllocators(TR::Node *node,
                                                                 TR::CodeGenerator *cg,
                                                                 int32_t leafArrayElementSize)
   {
   TR::Compilation *comp = cg->comp();
   TR_Debug *compDebug = comp->getDebug();
   TR_ASSERT_FATAL(comp->target().is64Bit(), "multianewArrayEvaluator is only supported on 64-bit JVMs!");
   TR_J9VMBase *fej9 = comp->fej9();

   // alignment requirement
   int32_t alignmentInBytes = TR::Compiler->om.getObjectAlignmentInBytes();

   // size of reference field and address
   int32_t referenceFieldSize = TR::Compiler->om.sizeofReferenceField();
   TR_ASSERT_FATAL(referenceFieldSize <= 8,
      "multianewArrayEvaluator - referenceFieldSize cannot be greater than 8!");
   int32_t addrSize = TR::Compiler->om.sizeofReferenceAddress();
   // we use 64 bit classes only in 64 bit arch without compression
   bool use64BitClasses = comp->target().is64Bit() && !TR::Compiler->om.generateCompressedObjectHeaders();
   TR::InstOpCode::Mnemonic storeClassOp = use64BitClasses ? TR::InstOpCode::std : TR::InstOpCode::stw;
   int8_t classPtrLen = use64BitClasses ? 8 : 4;

   // Zero size arrays are considered "discontiguous", and the "mustBeZero" field
   // of discontiguous arrays must be located where the "size" field of contiguous arrays is.
   UDATA offsetOfMustBeZeroField = fej9->getOffsetOfContiguousArraySizeField();

   // the maximum number of elements we can handle without risking an overflow
   uintptr_t maxObjectSizeInElements = cg->getMaxObjectSizeGuaranteedNotToOverflow() / referenceFieldSize;

   // the size of a length-0 array in bytes taking alignment into account
   int32_t zeroArraySizeAligned = OMR::align(TR::Compiler->om.discontiguousArrayHeaderSizeInBytes(),
                                             alignmentInBytes);

   // a length>0 array object would *not* require alignment if both a single element
   // and the header are already the exact multiple of alignment; otherwise alignment is needed
   bool needsAlignRefField = (OMR::align(referenceFieldSize, alignmentInBytes) != referenceFieldSize);
   bool needsAlignLeaf = (OMR::align(leafArrayElementSize, alignmentInBytes) != leafArrayElementSize);
   bool needsAlignHeader = (TR::Compiler->om.contiguousArrayHeaderSizeInBytes()
      != OMR::align(TR::Compiler->om.contiguousArrayHeaderSizeInBytes(), alignmentInBytes));
   // if an array needs alignment at runtime, we first add alignmentCompensation,
   // then mask with (-alignmentInBytes)
   int32_t alignmentCompensation = alignmentInBytes - 1;

   // offHeap enabled
#if defined(J9VM_GC_SPARSE_HEAP_ALLOCATION)
   bool isOffHeapAllocationEnabled = TR::Compiler->om.isOffHeapAllocationEnabled();
#endif /* defined(J9VM_GC_SPARSE_HEAP_ALLOCATION) */

   // ptr to array of sizes, with the highest dimension in the front
   TR::Register *dimsPtrReg = cg->evaluate(node->getFirstChild());
   // number of dimensions - compile time constant
   uint32_t nDims = node->getSecondChild()->get32bitIntegralValue();
   // class pointer of objects in the array - in the 2D case this is the class of the subarray
   TR::Register *classReg = cg->evaluate(node->getThirdChild());

   // points to the resulting array allocated
   TR::Register *targetReg = cg->allocateRegister();

   TR::Register *firstDimLenReg = cg->allocateRegister();
   TR::Register *secondDimLenReg = cg->allocateRegister();
   TR::Register *temp1Reg = cg->allocateRegister();
   TR::Register *temp2Reg = cg->allocateRegister();
   TR::Register *temp3Reg = cg->allocateRegister();
   TR::Register *subArraySizeReg = cg->allocateRegister();

   TR::Register *vmThreadReg = cg->getMethodMetaDataRegister();
   TR::Register *condReg = cg->allocateRegister(TR_CCR);

   TR::LabelSymbol *startLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *nonZeroFirstDimLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *nonZeroSecondDimLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *loopLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *loop2Label = generateLabelSymbol(cg);
   TR::LabelSymbol *endLabel = generateLabelSymbol(cg);

   // out of line labels
   TR::LabelSymbol *oolFailLabel = generateLabelSymbol(cg);
   // oolJumpLabel is a common point that all branches will jump to.
   // From this label, we branch to OOL code.
   // We do this instead of jumping directly to OOL code from mainline
   // since according to the x and z implementation that is not possible
   TR::LabelSymbol *oolJumpLabel = generateLabelSymbol(cg);

   startLabel->setStartInternalControlFlow();
   endLabel->setEndInternalControlFlow();
   generateLabelInstruction(cg, TR::InstOpCode::label, node, startLabel);

   // the fallback helper function, currently only used when there's a risk of heap overflow
   TR_PPCOutOfLineCodeSection *outlinedHelperCall = new (cg->trHeapMemory())
      TR_PPCOutOfLineCodeSection(node, TR::acall, targetReg, oolFailLabel, endLabel, cg);
   cg->getPPCOutOfLineCodeSectionList().push_front(outlinedHelperCall);

   // load the dimensions
   generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, firstDimLenReg,
      TR::MemoryReference::createWithDisplacement(cg, dimsPtrReg, 4, 4));
   generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, secondDimLenReg,
      TR::MemoryReference::createWithDisplacement(cg, dimsPtrReg, 0, 4));

   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, condReg, firstDimLenReg, 0);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, nonZeroFirstDimLabel, condReg);

   // if we reach here, both dimensions are zero, just allocate a zero-length object array
   generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, targetReg,
      TR::MemoryReference::createWithDisplacement(cg, vmThreadReg,
         offsetof(J9VMThread, heapAlloc), addrSize));

   // see if we will have a heap overflow, if so go back to the ool call
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, temp1Reg, targetReg,
                                  zeroArraySizeAligned);
   generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, temp2Reg,
      TR::MemoryReference::createWithDisplacement(cg, vmThreadReg,
         offsetof(J9VMThread, heapTop), addrSize));
   generateTrg1Src2Instruction(cg, TR::InstOpCode::Op_cmpl, node, condReg, temp1Reg, temp2Reg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bgt, node, oolJumpLabel, condReg);

   // update the heapAlloc pointer to point to the next free space
   generateMemSrc1Instruction(cg, TR::InstOpCode::Op_st, node,
      TR::MemoryReference::createWithDisplacement(cg, vmThreadReg,
         offsetof(J9VMThread, heapAlloc), addrSize), temp1Reg);

   // initialise the class field in the header
   generateMemSrc1Instruction(cg, storeClassOp, node,
      TR::MemoryReference::createWithDisplacement(cg, targetReg,
         TR::Compiler->om.offsetOfObjectVftField(), classPtrLen), classReg);
   // the size and mustBeZero ('0') fields should be zero by default, so is dataAddr for Offheap

   generateLabelInstruction(cg, TR::InstOpCode::b, node, endLabel);

   // === if we reach here, the first dimension is not zero
   generateLabelInstruction(cg, TR::InstOpCode::label, node, nonZeroFirstDimLabel);
   // jump away if the object size for the array object is too large
   // static cast and unsigned cmp copied from the x86 evaluator
   loadConstant(cg, node, static_cast<int32_t>(maxObjectSizeInElements), temp3Reg);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmpl4, node, condReg, firstDimLenReg, temp3Reg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bgt, node, oolJumpLabel, condReg);

   // check if we have enough space by accounting for the outer array object:
   //    1. the header of a contiguous array
   //    2. (firstDimLen * referenceFieldSize)
   //    3. alignment padding
   // temp1Reg = firstDimLenReg * referenceFieldSize; referenceFieldSize can only be 4 or 8
   if (referenceFieldSize == 8)
      generateShiftLeftImmediateLong(cg, node, temp1Reg, firstDimLenReg, 3);
   else
      generateShiftLeftImmediateLong(cg, node, temp1Reg, firstDimLenReg, 2);
   // add the header size, and the alignment compensation if needed
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, temp1Reg, temp1Reg,
      TR::Compiler->om.contiguousArrayHeaderSizeInBytes()
            + ((needsAlignHeader || needsAlignRefField) ? alignmentCompensation : 0));
   if (needsAlignHeader || needsAlignRefField) // do a mask to ensure alignment
      {
      generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldicr, node, temp1Reg, temp1Reg,
                                       0, -alignmentInBytes);
      }

   // jump away if the second dimension is not zero
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, condReg, secondDimLenReg, 0);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, nonZeroSecondDimLabel, condReg);

   // when the second dimension is zero, we allocate N zero-sized array headers padded for alignment
   // temp2Reg = firstDimLenReg * zeroArraySizeAligned
   if (zeroArraySizeAligned == 8 || zeroArraySizeAligned == 16) // we can do a shift most of the times
      generateShiftLeftImmediateLong(cg, node, temp2Reg, firstDimLenReg, trailingZeroes(zeroArraySizeAligned));
   else
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::mulli, node, temp2Reg, firstDimLenReg, zeroArraySizeAligned);
   // temp2Reg = temp2Reg + temp1Reg + (targetReg = heapAlloc) = where heapAlloc will endup
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, temp2Reg, temp2Reg, temp1Reg);
   generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, targetReg,
      TR::MemoryReference::createWithDisplacement(cg, vmThreadReg,
         offsetof(J9VMThread, heapAlloc), addrSize));
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, temp2Reg, temp2Reg, targetReg);
   // then, load the heapTop to see if we still have space; the secondDimLenReg used as temp
   generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, secondDimLenReg,
      TR::MemoryReference::createWithDisplacement(cg, vmThreadReg,
         offsetof(J9VMThread, heapTop), addrSize));
   generateTrg1Src2Instruction(cg, TR::InstOpCode::Op_cmpl, node, condReg, temp2Reg, secondDimLenReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bgt, node, oolJumpLabel, condReg);
   // update the heapAlloc pointer to point to the next free space
   generateMemSrc1Instruction(cg, TR::InstOpCode::Op_st, node,
      TR::MemoryReference::createWithDisplacement(cg, vmThreadReg,
         offsetof(J9VMThread, heapAlloc), addrSize), temp2Reg);

   // initialise the first dimension array's header's class and size
   generateMemSrc1Instruction(cg, storeClassOp, node,
      TR::MemoryReference::createWithDisplacement(cg, targetReg,
         TR::Compiler->om.offsetOfObjectVftField(), classPtrLen), classReg);
   generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node,
      TR::MemoryReference::createWithDisplacement(cg, targetReg,
         fej9->getOffsetOfContiguousArraySizeField(), 4), firstDimLenReg);

   // load the component class
   TR::Register *componentClassReg = secondDimLenReg; // we don't need the secondDimLenReg anymore
   generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, componentClassReg,
      TR::MemoryReference::createWithDisplacement(cg, classReg,
         offsetof(J9ArrayClass, componentType), addrSize));

   // temp2Reg = targetReg + temp1Reg = start of the 2nd dimension headers
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, temp2Reg, temp1Reg, targetReg);
   // temp1Reg points to the first element of the 1st dimension array by jumping over the header
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, temp1Reg, targetReg,
      TR::Compiler->om.contiguousArrayHeaderSizeInBytes());

#if defined(J9VM_GC_SPARSE_HEAP_ALLOCATION)
   if (isOffHeapAllocationEnabled)
      {
      // the dataAddr field of the 1st dimension array, which is non-zero and hence contiguous,
      // should point to the first element of the array i.e. temp1Reg
      generateMemSrc1Instruction(cg, TR::InstOpCode::Op_st, node,
         TR::MemoryReference::createWithDisplacement(cg, targetReg,
            fej9->getOffsetOfContiguousDataAddrField(), addrSize), temp1Reg);
      }
#endif /* J9VM_GC_SPARSE_HEAP_ALLOCATION */

   // --- ready the loop
   generateSrc1Instruction(cg, TR::InstOpCode::mtctr, node, firstDimLenReg);

   // === loop
   generateLabelInstruction(cg, TR::InstOpCode::label, node, loopLabel);
   // initialise a header in the second dimension with class = component
   generateMemSrc1Instruction(cg, storeClassOp, node,
      TR::MemoryReference::createWithDisplacement(cg, temp2Reg,
         TR::Compiler->om.offsetOfObjectVftField(), classPtrLen), componentClassReg);
   // size and mustBeZero are already zero by default, so is dataAddr for Offheap

   // Store 2nd dim element into 1st dim array slot, compress temp2 if needed
   if (comp->useCompressedPointers()) // only possible with 64-bit
      {
      int32_t shiftAmount = TR::Compiler->om.compressedReferenceShift();
      if (shiftAmount != 0)
         {
         // firstDimLenReg can be used as a temp
         generateShiftRightLogicalImmediateLong(cg, node, firstDimLenReg, temp2Reg, shiftAmount);
         generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node,
            TR::MemoryReference::createWithDisplacement(cg, temp1Reg, 0, 4), firstDimLenReg);
         }
      else
         {
         generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node,
            TR::MemoryReference::createWithDisplacement(cg, temp1Reg, 0, 4), temp2Reg);
         }
      }
   else
      {
      generateMemSrc1Instruction(cg, TR::InstOpCode::Op_st, node,
         TR::MemoryReference::createWithDisplacement(cg, temp1Reg, 0, addrSize), temp2Reg);
      }

   // index cursors temp1 and temp2
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, temp1Reg, temp1Reg, referenceFieldSize);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, temp2Reg, temp2Reg, zeroArraySizeAligned);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bdnz, node, loopLabel, condReg);

   generateLabelInstruction(cg, TR::InstOpCode::b, node, endLabel);

   // === if we reach here, the second dimension is not zero
   generateLabelInstruction(cg, TR::InstOpCode::label, node, nonZeroSecondDimLabel);

   // jump away if the object size for a subarray object is too large
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmpl4, node, condReg, secondDimLenReg, temp3Reg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bgt, node, oolJumpLabel, condReg);

   // We need N subarrays; each subarray should contain the following:
   //    1. header of a second dimension array
   //    2. (secondDimLen * leafArrayElementSize)
   //    3. alignment padding
   // temp2Reg = secondDimLenReg * leafArrayElementSize
   if (leafArrayElementSize != 1)
      {
      generateShiftLeftImmediateLong(cg, node, temp2Reg, secondDimLenReg, trailingZeroes(leafArrayElementSize));
      // add alignment compensation, and the header size
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, subArraySizeReg, temp2Reg,
         TR::Compiler->om.contiguousArrayHeaderSizeInBytes()
            + ((needsAlignHeader || needsAlignLeaf) ? alignmentCompensation : 0));
      }
   else // save an instruction if the leafArrayElementSize is 1
      {
      // add alignment compensation, and the header size
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, subArraySizeReg, secondDimLenReg,
         TR::Compiler->om.contiguousArrayHeaderSizeInBytes()
            + ((needsAlignHeader || needsAlignLeaf) ? alignmentCompensation : 0));
      }
   if (needsAlignHeader || needsAlignLeaf) // do a mask to ensure alignment
      {
      generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldicr, node, subArraySizeReg, subArraySizeReg,
                                       0, -alignmentInBytes);
      }
   // temp2Reg = subArraySizeReg * firstDimLenReg = the space required for all subarrays
   generateTrg1Src2Instruction(cg, TR::InstOpCode::mulld, node, temp2Reg, subArraySizeReg, firstDimLenReg);

   // temp2Reg = temp2Reg + temp1Reg + (targetReg = heapAlloc) = where heapAlloc will endup
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, temp2Reg, temp2Reg, temp1Reg);
   generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, targetReg,
      TR::MemoryReference::createWithDisplacement(cg, vmThreadReg,
         offsetof(J9VMThread, heapAlloc), addrSize));
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, temp2Reg, temp2Reg, targetReg);
   // then, load the heapTop to see if we still have space
   generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, temp3Reg,
      TR::MemoryReference::createWithDisplacement(cg, vmThreadReg,
         offsetof(J9VMThread, heapTop), addrSize));
   generateTrg1Src2Instruction(cg, TR::InstOpCode::Op_cmpl, node, condReg, temp2Reg, temp3Reg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bgt, node, oolJumpLabel, condReg);
   // update the heapAlloc pointer to point to the next free space
   generateMemSrc1Instruction(cg, TR::InstOpCode::Op_st, node,
      TR::MemoryReference::createWithDisplacement(cg, vmThreadReg,
         offsetof(J9VMThread, heapAlloc), addrSize), temp2Reg);

   // initialise the first dimension array's header's class and size
   generateMemSrc1Instruction(cg, storeClassOp, node,
      TR::MemoryReference::createWithDisplacement(cg, targetReg,
         TR::Compiler->om.offsetOfObjectVftField(), classPtrLen), classReg);
   generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node,
      TR::MemoryReference::createWithDisplacement(cg, targetReg,
         fej9->getOffsetOfContiguousArraySizeField(), 4), firstDimLenReg);

   // load the component class
   componentClassReg = temp3Reg;
   generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, componentClassReg,
      TR::MemoryReference::createWithDisplacement(cg, classReg,
         offsetof(J9ArrayClass, componentType), addrSize));

   // temp2Reg = targetReg + temp1Reg = start of the 2nd dimension subarrays
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, temp2Reg, temp1Reg, targetReg);
   // temp1Reg points to the first element of the 1st dimension array by jumping over the header
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, temp1Reg, targetReg,
      TR::Compiler->om.contiguousArrayHeaderSizeInBytes());
#if defined(J9VM_GC_SPARSE_HEAP_ALLOCATION)
   if (isOffHeapAllocationEnabled)
      {
      // the dataAddr field of the 1st dimension array, which is non-zero and hence contiguous,
      // should point to the first element of the array i.e. temp1Reg
      generateMemSrc1Instruction(cg, TR::InstOpCode::Op_st, node,
         TR::MemoryReference::createWithDisplacement(cg, targetReg,
            fej9->getOffsetOfContiguousDataAddrField(), addrSize), temp1Reg);
      }
#endif /* J9VM_GC_SPARSE_HEAP_ALLOCATION */

   // --- ready the loop
   generateSrc1Instruction(cg, TR::InstOpCode::mtctr, node, firstDimLenReg);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, loop2Label);
   // initialise a header in the second dimension with class = component
   generateMemSrc1Instruction(cg, storeClassOp, node,
      TR::MemoryReference::createWithDisplacement(cg, temp2Reg,
         TR::Compiler->om.offsetOfObjectVftField(), classPtrLen), componentClassReg);
   // mustBeZero is already zero by default
   // all the elements in the subarray are also null by default, hooray for no initialisation needed
   generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node,
      TR::MemoryReference::createWithDisplacement(cg, temp2Reg,
         fej9->getOffsetOfContiguousArraySizeField(), 4), secondDimLenReg);

#if defined(J9VM_GC_SPARSE_HEAP_ALLOCATION)
   if (isOffHeapAllocationEnabled)
      {
      // the dataAddr field of the 2nd dimension subarray, which is non-zero and hence contiguous,
      // should point to where the first element should start, i.e. over the header
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, firstDimLenReg, temp2Reg,
         TR::Compiler->om.contiguousArrayHeaderSizeInBytes());
      generateMemSrc1Instruction(cg, TR::InstOpCode::Op_st, node,
         TR::MemoryReference::createWithDisplacement(cg, temp2Reg,
            fej9->getOffsetOfContiguousDataAddrField(), addrSize), firstDimLenReg);
      }
#endif /* J9VM_GC_SPARSE_HEAP_ALLOCATION */

   // Store 2nd dim element into 1st dim array slot, compress temp2 if needed
   if (comp->useCompressedPointers()) // only possible with 64-bit
      {
      int32_t shiftAmount = TR::Compiler->om.compressedReferenceShift();
      if (shiftAmount != 0)
         {
         // firstDimLenReg can be used as a temp
         generateShiftRightLogicalImmediateLong(cg, node, firstDimLenReg, temp2Reg, shiftAmount);
         generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node,
            TR::MemoryReference::createWithDisplacement(cg, temp1Reg, 0, 4), firstDimLenReg);
         }
      else
         {
         generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node,
            TR::MemoryReference::createWithDisplacement(cg, temp1Reg, 0, 4), temp2Reg);
         }
      }
   else
      {
      generateMemSrc1Instruction(cg, TR::InstOpCode::Op_st, node,
         TR::MemoryReference::createWithDisplacement(cg, temp1Reg, 0, addrSize), temp2Reg);
      }
   // index cursors temp1 and temp2
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, temp1Reg, temp1Reg, referenceFieldSize);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, temp2Reg, temp2Reg, subArraySizeReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bdnz, node, loop2Label, condReg);

   generateLabelInstruction(cg, TR::InstOpCode::b, node, endLabel);

   // === jump point because according to the x and z implementation we can't jump straight to oolFail
   generateLabelInstruction(cg, TR::InstOpCode::label, node, oolJumpLabel);
   generateLabelInstruction(cg, TR::InstOpCode::b, node, oolFailLabel);

   // we need to include post dependencies from the call node if they didn't copy the children nodes
   // (in which case they should be handled inside the function), and they use registers different
   // from the main line
   // I don't know if this is possible at all, every time I tested the results are just all zeros
   TR::Node *callNode = outlinedHelperCall->getCallNode();
   int callUsesFirstChild = (callNode->getFirstChild() == node->getFirstChild()) &&
                            (callNode->getFirstChild()->getRegister()) &&
                            (callNode->getFirstChild()->getRegister() != dimsPtrReg);
   int callUsesSecondChild = (callNode->getSecondChild() == node->getSecondChild()) &&
                            (callNode->getSecondChild()->getRegister());
   int callUsesThirdChild = (callNode->getThirdChild() == node->getThirdChild()) &&
                            (callNode->getThirdChild()->getRegister()) &&
                            (callNode->getThirdChild()->getRegister() != classReg);
   int numDeps = 12 + callUsesFirstChild + callUsesSecondChild + callUsesThirdChild;

   TR::RegisterDependencyConditions *dependencies =
      new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, numDeps, cg->trMemory());
   dependencies->addPostCondition(dimsPtrReg, TR::RealRegister::NoReg);
   dependencies->getPostConditions()->getRegisterDependency(dependencies->getAddCursorForPost() - 1)->setExcludeGPR0();

   dependencies->addPostCondition(classReg, TR::RealRegister::NoReg);
   dependencies->getPostConditions()->getRegisterDependency(dependencies->getAddCursorForPost() - 1)->setExcludeGPR0();


   dependencies->addPostCondition(targetReg, TR::RealRegister::NoReg);
   dependencies->getPostConditions()->getRegisterDependency(dependencies->getAddCursorForPost() - 1)->setExcludeGPR0();

   dependencies->addPostCondition(firstDimLenReg, TR::RealRegister::NoReg);
   dependencies->addPostCondition(secondDimLenReg, TR::RealRegister::NoReg);

   dependencies->addPostCondition(temp1Reg, TR::RealRegister::NoReg);
   dependencies->getPostConditions()->getRegisterDependency(dependencies->getAddCursorForPost() - 1)->setExcludeGPR0();

   dependencies->addPostCondition(temp2Reg, TR::RealRegister::NoReg);
   dependencies->getPostConditions()->getRegisterDependency(dependencies->getAddCursorForPost() - 1)->setExcludeGPR0();
   dependencies->addPostCondition(temp3Reg, TR::RealRegister::NoReg);
   dependencies->getPostConditions()->getRegisterDependency(dependencies->getAddCursorForPost() - 1)->setExcludeGPR0();

   dependencies->addPostCondition(subArraySizeReg, TR::RealRegister::NoReg);
   dependencies->addPostCondition(condReg, TR::RealRegister::NoReg);

   TR::Register *reg;
   if (callUsesFirstChild)
      {
      dependencies->addPostCondition(callNode->getFirstChild()->getRegister(),
                                     TR::RealRegister::NoReg);
      }
   if (callUsesSecondChild)
      {
      dependencies->addPostCondition(callNode->getSecondChild()->getRegister(),
                                     TR::RealRegister::NoReg);
      }
   if (callUsesThirdChild)
      {
      dependencies->addPostCondition(callNode->getThirdChild()->getRegister(),
                                     TR::RealRegister::NoReg);
      }

   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, endLabel, dependencies);

   // Copy the newly allocated object into a collected reference register
   TR::Register *targetRegisterFinal = cg->allocateCollectedReferenceRegister();
   TR::RegisterDependencyConditions *finalDependency =
      new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, 1, cg->trMemory());
   finalDependency->addPostCondition(targetRegisterFinal, TR::RealRegister::NoReg);
   generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, targetRegisterFinal, targetReg);

   cg->stopUsingRegister(targetReg);
   cg->stopUsingRegister(dimsPtrReg);
   cg->stopUsingRegister(classReg);
   cg->stopUsingRegister(firstDimLenReg);
   cg->stopUsingRegister(secondDimLenReg);
   cg->stopUsingRegister(temp1Reg);
   cg->stopUsingRegister(temp2Reg);
   cg->stopUsingRegister(temp3Reg);
   cg->stopUsingRegister(subArraySizeReg);
   cg->stopUsingRegister(condReg);

   cg->decReferenceCount(node->getFirstChild());
   cg->decReferenceCount(node->getSecondChild());
   cg->decReferenceCount(node->getThirdChild());

   node->setRegister(targetRegisterFinal);
   return targetRegisterFinal;
   }


TR::Register *J9::Power::TreeEvaluator::multianewArrayEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());
   TR_ASSERT_FATAL(comp->target().is64Bit(), "multianewArrayEvaluator is only supported on 64-bit JVMs!");

   // Number of dimensions - this is fixed in the bytecode, so compile time constant
   TR::Node *secondChild = node->getSecondChild();
   // The number of dimensions should always be an iconst
   TR_ASSERT_FATAL(secondChild->getOpCodeValue() == TR::iconst, "dims of multianewarray must be iconst");
   uint32_t nDims = secondChild->get32bitIntegralValue();
   static bool disableInlineMultianewArray = feGetEnv("TR_DisableInlineMultianewArray") != NULL;

   // Get the size of the elements in the leaf components
   int32_t leafArrayElementSize = TR::Compiler->om.getTwoDimensionalArrayComponentSize(node->getThirdChild());

   // Anything with more than 2 dimensions will be replaced by a direct call when lowering trees,
   // so this is functionally equivalent of saying only inline if the dimension is exactly 2.
   // We also need to make sure the TLH is properly zeroed.
   // Finally, we need to be sure we know the elementSize
   if (nDims > 1 && !disableInlineMultianewArray
         && fej9->tlhHasBeenCleared() && !comp->getOptions()->realTimeGC()
         && leafArrayElementSize != -1)
      {
      return generateMultianewArrayWithInlineAllocators(node, cg, leafArrayElementSize);
      }
   else
      {
      if (comp->getOption(TR_TraceCG))
         {
         traceMsg(comp, "Disabling inline allocations for multianewarray of dim %d\n", nDims);
         }
      TR::ILOpCodes opCode = node->getOpCodeValue();
      TR::Node::recreate(node, TR::acall);
      TR::Register *targetRegister = TR::TreeEvaluator::performCall(node, false, cg);
      TR::Node::recreate(node, opCode);
      return targetRegister;
      }
   }

TR::Register *J9::Power::TreeEvaluator::arraylengthEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR_ASSERT(cg->comp()->requiresSpineChecks(), "TR::arraylength should be lowered when hybrid arraylets are not in use");
   TR_ASSERT(node->getOpCodeValue() == TR::arraylength, "arraylengthEvaluator expecting TR::arraylength");

   TR::Register *objectReg = cg->evaluate(node->getFirstChild());
   TR::Register *lengthReg = cg->allocateRegister();
   TR::Register *condReg = cg->allocateRegister(TR_CCR);
   TR::LabelSymbol *doneLabel = generateLabelSymbol(cg);
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());

   TR::MemoryReference *contiguousArraySizeMR = TR::MemoryReference::createWithDisplacement(cg, objectReg, fej9->getOffsetOfContiguousArraySizeField(), 4);
   TR::MemoryReference *discontiguousArraySizeMR = TR::MemoryReference::createWithDisplacement(cg, objectReg, fej9->getOffsetOfDiscontiguousArraySizeField(), 4);

   TR::RegisterDependencyConditions *deps = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, 3, cg->trMemory());
   deps->addPostCondition(objectReg, TR::RealRegister::NoReg);
   deps->getPostConditions()->getRegisterDependency(0)->setExcludeGPR0();
   deps->addPostCondition(lengthReg, TR::RealRegister::NoReg);
   deps->addPostCondition(condReg, TR::RealRegister::NoReg);

   // lwz R, [B + contiguousSize]       ; Load contiguous array length
   // cmp R, 0                          ; If 0, must be a discontiguous array
   // beq out-of-line
   // done:
   //
   // out-of-line:
   // lwz R, [B + discontiguousSize]    ; Load discontiguous array length
   // b done

   TR::LabelSymbol *discontiguousArrayLabel = generateLabelSymbol(cg);
   TR_PPCOutOfLineCodeSection *discontiguousArrayOOL = new (cg->trHeapMemory()) TR_PPCOutOfLineCodeSection(discontiguousArrayLabel, doneLabel, cg);
   cg->getPPCOutOfLineCodeSectionList().push_front(discontiguousArrayOOL);

   generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, lengthReg, contiguousArraySizeMR);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpli4, node, condReg, lengthReg, 0);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, PPCOpProp_BranchUnlikely, node, discontiguousArrayLabel, condReg);
   generateLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel);

   // OOL begin.
   //
   discontiguousArrayOOL->swapInstructionListsWithCompilation();
      {
      generateLabelInstruction(cg, TR::InstOpCode::label, node, discontiguousArrayLabel);
      generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, lengthReg, discontiguousArraySizeMR);
      generateLabelInstruction(cg, TR::InstOpCode::b, node, doneLabel);
      }
   discontiguousArrayOOL->swapInstructionListsWithCompilation();
   //
   // OOL end.

   TR::LabelSymbol *depLabel = generateLabelSymbol(cg);
   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, depLabel, deps);

   cg->stopUsingRegister(condReg);
   cg->decReferenceCount(node->getFirstChild());
   node->setRegister(lengthReg);

   return lengthReg;
   }

TR::Register *J9::Power::TreeEvaluator::DIVCHKEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR::Compilation *comp = cg->comp();
   TR::Node *secondChild = node->getFirstChild()->getSecondChild();
   TR::DataType type = secondChild->getType();
   TR::Register *srcReg;
   TR::Instruction *gcPoint;
   bool constDivisor = secondChild->getOpCode().isLoadConst();
   bool killSrc = false;

   if (!constDivisor || (type.isInt32() && secondChild->getInt() == 0) || (type.isInt64() && secondChild->getLongInt() == 0))
      {
      if (!constDivisor || cg->getHasResumableTrapHandler())
         {
         srcReg = cg->evaluate(secondChild);
         if (type.isInt64() && comp->target().is32Bit())
            {
            TR::Register *trgReg = cg->allocateRegister();
            generateTrg1Src2Instruction(cg, TR::InstOpCode::OR, node, trgReg, srcReg->getHighOrder(), srcReg->getLowOrder());
            srcReg = trgReg;
            killSrc = true;
            }
         }

      if (cg->getHasResumableTrapHandler())
         {
         if (type.isInt64() && comp->target().is64Bit())
            gcPoint = generateSrc1Instruction(cg, TR::InstOpCode::tdllti, node, srcReg, 1);
         else
            gcPoint = generateSrc1Instruction(cg, TR::InstOpCode::twllti, node, srcReg, 1);
         cg->setCanExceptByTrap();
         }
      else
         {
         TR::LabelSymbol *snippetLabel = cg->lookUpSnippet(TR::Snippet::IsHelperCall, node->getSymbolReference());
         TR::RegisterDependencyConditions *conditions = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(1, 1, cg->trMemory());
         TR::Register *jumpReg = cg->allocateRegister();

         if (snippetLabel == NULL)
            {
            snippetLabel = generateLabelSymbol(cg);
            cg->addSnippet(new (cg->trHeapMemory()) TR::PPCHelperCallSnippet(cg, node, snippetLabel, node->getSymbolReference()));
            }

         // trampoline kills gr11
         TR::addDependency(conditions, jumpReg, TR::RealRegister::gr11, TR_GPR, cg);
         if (constDivisor)
            {
            // Can be improved to: call the helper directly.
            gcPoint = generateDepLabelInstruction(cg, TR::InstOpCode::bl, node, snippetLabel, conditions);
            }
         else
            {
            TR::Register *condReg = cg->allocateRegister(TR_CCR);
            if (type.isInt64() && comp->target().is64Bit())
               generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpli8, node, condReg, srcReg, 0);
            else
               generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpli4, node, condReg, srcReg, 0);
            gcPoint = generateDepConditionalBranchInstruction(cg, TR::InstOpCode::beql, PPCOpProp_BranchUnlikely, node, snippetLabel, condReg, conditions);
            cg->stopUsingRegister(condReg);
            }
         cg->stopUsingRegister(jumpReg);
         }

      if (killSrc)
         cg->stopUsingRegister(srcReg);

      gcPoint->PPCNeedsGCMap(0xFFFFFFFF);
      }

   cg->evaluate(node->getFirstChild());
   cg->decReferenceCount(node->getFirstChild());
   return NULL;
   }

static void genBoundCheck(TR::CodeGenerator *cg, TR::Node *node, TR::Register *indexReg, int32_t indexVal, TR::Register *arrayLengthReg, int32_t arrayLengthVal,
      TR::Register *condReg, bool noTrap)
   {
   TR::Instruction *gcPoint;
   if (noTrap)
      {
      TR::LabelSymbol *boundCheckFailSnippetLabel = cg->lookUpSnippet(TR::Snippet::IsHelperCall, node->getSymbolReference());
      if (!boundCheckFailSnippetLabel)
         {
         boundCheckFailSnippetLabel = generateLabelSymbol(cg);
         cg->addSnippet(new (cg->trHeapMemory()) TR::PPCHelperCallSnippet(cg, node, boundCheckFailSnippetLabel, node->getSymbolReference()));
         }

      if (indexReg)
         generateTrg1Src2Instruction(cg, TR::InstOpCode::cmpl4, node, condReg, arrayLengthReg, indexReg);
      else
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpli4, node, condReg, arrayLengthReg, indexVal);

      // NOTE: Trampoline kills gr11
      gcPoint = generateConditionalBranchInstruction(cg, TR::InstOpCode::blel, PPCOpProp_BranchUnlikely, node, boundCheckFailSnippetLabel, condReg);
      }
   else
      {
      if (indexReg)
         {
         if (arrayLengthReg)
            gcPoint = generateSrc2Instruction(cg, TR::InstOpCode::twlle, node, arrayLengthReg, indexReg);
         else
            gcPoint = generateSrc1Instruction(cg, TR::InstOpCode::twlgei, node, indexReg, arrayLengthVal);
         }
      else
         {
         if (arrayLengthReg)
            gcPoint = generateSrc1Instruction(cg, TR::InstOpCode::twllei, node, arrayLengthReg, indexVal);
         else if (arrayLengthVal <= indexVal)
            gcPoint = generateInstruction(cg, TR::InstOpCode::trap, node);
         }

      cg->setCanExceptByTrap();
      }

   // Exception edges don't have any live regs
   gcPoint->PPCNeedsGCMap(0);
   }

TR::Register *J9::Power::TreeEvaluator::BNDCHKEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   static bool noReversedTrap = (feGetEnv("TR_noReversedTrap") != NULL);
   TR::Node *secondChild = node->getSecondChild();
   TR::Node *firstChild = node->getFirstChild();
   TR::Register *src1Reg;
   TR::Register *src2Reg = NULL;
   TR::Register *cndReg, *tmpReg = NULL;
   int32_t value;
   TR::RegisterDependencyConditions *conditions;
   TR::LabelSymbol *snippetLabel;
   TR::Instruction *gcPoint;
   bool noTrap = !cg->getHasResumableTrapHandler();
   bool reversed = false;

   if (noTrap)
      {
      cndReg = cg->allocateRegister(TR_CCR);
      conditions = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(4, 4, cg->trMemory());
      }

   if (firstChild->getOpCode().isLoadConst() && firstChild->getInt() >= 0 && firstChild->getInt() <= UPPER_IMMED && firstChild->getRegister() == NULL && !noReversedTrap)
      {
      src2Reg = cg->evaluate(secondChild);
      reversed = true;
      }
   else
      {
      src1Reg = cg->evaluate(firstChild);

      if (secondChild->getOpCode().isLoadConst() && secondChild->getRegister() == NULL)
         {
         value = secondChild->getInt();
         if (value < 0 || value > UPPER_IMMED)
            {
            src2Reg = cg->evaluate(secondChild);
            }
         }
      else
         src2Reg = cg->evaluate(secondChild);
      }

   if (!noTrap)
      {
      if (reversed)
         {
         gcPoint = generateSrc1Instruction(cg, TR::InstOpCode::twlgei, node, src2Reg, firstChild->getInt());
         }
      else
         {
         if (src2Reg == NULL)
            gcPoint = generateSrc1Instruction(cg, TR::InstOpCode::twllei, node, src1Reg, value);
         else
            gcPoint = generateSrc2Instruction(cg, TR::InstOpCode::twlle, node, src1Reg, src2Reg);
         }
      }
   else
      {
      if (reversed)
         {
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpli4, node, cndReg, src2Reg, firstChild->getInt());
         }
      else
         {
         if (src2Reg == NULL)
            generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpli4, node, cndReg, src1Reg, value);
         else
            generateTrg1Src2Instruction(cg, TR::InstOpCode::cmpl4, node, cndReg, src1Reg, src2Reg);
         }

      snippetLabel = cg->lookUpSnippet(TR::Snippet::IsHelperCall, node->getSymbolReference());
      if (snippetLabel == NULL)
         {
         snippetLabel = generateLabelSymbol(cg);
         cg->addSnippet(new (cg->trHeapMemory()) TR::PPCHelperCallSnippet(cg, node, snippetLabel, node->getSymbolReference()));
         }

      tmpReg = cg->allocateRegister();
      // trampoline kills gr11
      TR::addDependency(conditions, tmpReg, TR::RealRegister::gr11, TR_GPR, cg);
      gcPoint = generateDepConditionalBranchInstruction(cg, reversed ? TR::InstOpCode::bgel : TR::InstOpCode::blel, PPCOpProp_BranchUnlikely, node, snippetLabel, cndReg, conditions);
      cg->stopUsingRegister(tmpReg);
      }

   if (noTrap)
      cg->stopUsingRegister(cndReg);

   gcPoint->PPCNeedsGCMap(0xFFFFFFFF);
   if (!noTrap)
      cg->setCanExceptByTrap();
   cg->decReferenceCount(firstChild);
   cg->decReferenceCount(secondChild);
   secondChild->setIsNonNegative(true);
   return (NULL);
   }

TR::Register *J9::Power::TreeEvaluator::ArrayCopyBNDCHKEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR::Node *firstChild = node->getFirstChild();
   TR::Node *secondChild = node->getSecondChild();
   TR::LabelSymbol *snippetLabel = NULL;
   TR::Instruction *gcPoint;
   bool noTrap = !cg->getHasResumableTrapHandler();
   bool directCase = firstChild->getOpCode().isLoadConst() && secondChild->getOpCode().isLoadConst() && firstChild->getInt() < secondChild->getInt();

   if (directCase || noTrap)
      {
      snippetLabel = cg->lookUpSnippet(TR::Snippet::IsHelperCall, node->getSymbolReference());
      if (snippetLabel == NULL)
         {
         snippetLabel = generateLabelSymbol(cg);
         cg->addSnippet(new (cg->trHeapMemory()) TR::PPCHelperCallSnippet(cg, node, snippetLabel, node->getSymbolReference()));
         }
      }

   // We are checking for firstChild>=secondChild. We don't need to copy registers.

   if (directCase)
      {
      gcPoint = generateLabelInstruction(cg, TR::InstOpCode::bl, node, snippetLabel);
      }
   else if (firstChild->getOpCode().isLoadConst() && firstChild->getInt() >= LOWER_IMMED && firstChild->getInt() <= UPPER_IMMED && firstChild->getRegister() == NULL)
      {
      TR::Register *copyIndexReg = cg->evaluate(secondChild);
      if (noTrap)
         {
         TR::Register *cndReg = cg->allocateRegister(TR_CCR);
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, cndReg, copyIndexReg, firstChild->getInt());
         gcPoint = generateConditionalBranchInstruction(cg, TR::InstOpCode::bgtl, PPCOpProp_BranchUnlikely, node, snippetLabel, cndReg);
         cg->stopUsingRegister(cndReg);
         }
      else
         {
         gcPoint = generateSrc1Instruction(cg, TR::InstOpCode::twgti, node, copyIndexReg, firstChild->getInt());
         cg->setCanExceptByTrap();
         }
      }
   else
      {
      TR::Register *boundReg = cg->evaluate(firstChild);
      if (secondChild->getOpCode().isLoadConst() && secondChild->getInt() >= LOWER_IMMED && secondChild->getInt() <= UPPER_IMMED)
         {
         if (noTrap)
            {
            TR::Register *cndReg = cg->allocateRegister(TR_CCR);
            generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, cndReg, boundReg, secondChild->getInt());
            gcPoint = generateConditionalBranchInstruction(cg, TR::InstOpCode::bltl, PPCOpProp_BranchUnlikely, node, snippetLabel, cndReg);
            cg->stopUsingRegister(cndReg);
            }
         else
            {
            gcPoint = generateSrc1Instruction(cg, TR::InstOpCode::twlti, node, boundReg, secondChild->getInt());
            cg->setCanExceptByTrap();
            }
         }
      else
         {
         TR::Register *copyIndexReg = cg->evaluate(secondChild);
         if (noTrap)
            {
            TR::Register *cndReg = cg->allocateRegister(TR_CCR);
            generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp4, node, cndReg, boundReg, copyIndexReg);
            gcPoint = generateConditionalBranchInstruction(cg, TR::InstOpCode::bltl, PPCOpProp_BranchUnlikely, node, snippetLabel, cndReg);
            cg->stopUsingRegister(cndReg);
            }
         else
            {
            gcPoint = generateSrc2Instruction(cg, TR::InstOpCode::twlt, node, boundReg, copyIndexReg);
            cg->setCanExceptByTrap();
            }
         }
      }

   gcPoint->PPCNeedsGCMap(0xFFFFFFFF);
   cg->decReferenceCount(firstChild);
   cg->decReferenceCount(secondChild);
   if (secondChild->getOpCode().isLoadConst() && secondChild->getInt() >= 0)
      firstChild->setIsNonNegative(true);
   return (NULL);
   }

static TR::Instruction* genSpineCheck(TR::CodeGenerator *cg, TR::Node *node, TR::Register *arrayLengthReg, TR::Register *condReg, TR::LabelSymbol *discontiguousArrayLabel)
   {
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpli4, node, condReg, arrayLengthReg, 0);
   return generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, discontiguousArrayLabel, condReg);
   }

static TR::Instruction* genSpineCheck(TR::CodeGenerator *cg, TR::Node *node, TR::Register *baseArrayReg, TR::Register *arrayLengthReg, TR::Register *condReg,
      TR::LabelSymbol *discontiguousArrayLabel)
   {
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());
   TR::MemoryReference *contiguousArraySizeMR = TR::MemoryReference::createWithDisplacement(cg, baseArrayReg, fej9->getOffsetOfContiguousArraySizeField(), 4);
   generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, arrayLengthReg, contiguousArraySizeMR);
   return genSpineCheck(cg, node, arrayLengthReg, condReg, discontiguousArrayLabel);
   }

static void genArrayletAccessAddr(TR::CodeGenerator *cg, TR::Node *node, int32_t elementSize,
// Inputs:
      TR::Register *baseArrayReg, TR::Register *indexReg, int32_t indexVal,
      // Outputs:
      TR::Register *arrayletReg, TR::Register *offsetReg, int32_t& offsetVal)
   {
   TR::Compilation* comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());
   TR_ASSERT(offsetReg || !indexReg, "Expecting valid offset reg when index reg is passed");

   uintptr_t arrayHeaderSize = TR::Compiler->om.discontiguousArrayHeaderSizeInBytes();
   int32_t spinePointerSize = TR::Compiler->om.sizeofReferenceField();
   int32_t spinePointerSizeShift = spinePointerSize == 8 ? 3 : 2;

   TR::MemoryReference *spineMR;

   // Calculate the spine offset.
   //
   if (indexReg)
      {
      int32_t spineShift = fej9->getArraySpineShift(elementSize);

      // spineOffset = (index >> spineShift) * spinePtrSize
      //             = (index >> spineShift) << spinePtrSizeShift
      //             = (index >> (spineShift - spinePtrSizeShift)) & ~(spinePtrSize - 1)
      // spineOffset += arrayHeaderSize
      //
      TR_ASSERT(spineShift >= spinePointerSizeShift, "Unexpected spine shift value");
      generateShiftRightLogicalImmediate(cg, node, arrayletReg, indexReg, spineShift - spinePointerSizeShift);
      generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, arrayletReg, arrayletReg, 0, ~(spinePointerSize - 1));
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, arrayletReg, arrayletReg, arrayHeaderSize);

      spineMR = TR::MemoryReference::createWithIndexReg(cg, baseArrayReg, arrayletReg, spinePointerSize);
      }
   else
      {
      int32_t spineIndex = fej9->getArrayletLeafIndex(indexVal, elementSize);
      int32_t spineDisp32 = spineIndex * spinePointerSize + arrayHeaderSize;

      spineMR = TR::MemoryReference::createWithDisplacement(cg, baseArrayReg, spineDisp32, spinePointerSize);
      }

   // Load the arraylet from the spine.
   //
   generateTrg1MemInstruction(cg, spinePointerSize == 8 ? TR::InstOpCode::ld : TR::InstOpCode::lwz, node, arrayletReg, spineMR);

   // Calculate the arraylet offset.
   //
   if (indexReg)
      {
      int32_t arrayletMask = fej9->getArrayletMask(elementSize);
      int32_t elementSizeShift = CHAR_BIT * sizeof(int32_t) - leadingZeroes(elementSize - 1);

      generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, offsetReg, indexReg, elementSizeShift, arrayletMask << elementSizeShift);
      }
   else
      offsetVal = (fej9->getLeafElementIndex(indexVal, elementSize) * elementSize);
   }

static TR::InstOpCode::Mnemonic getLoadOrStoreFromDataType(TR::CodeGenerator *cg, TR::DataType dt, int32_t elementSize, bool isUnsigned, bool returnLoad)
   {
   switch (dt)
      {
   case TR::Int8:
      return returnLoad ? TR::InstOpCode::lbz : TR::InstOpCode::stb;
   case TR::Int16:
      if (returnLoad)
         return isUnsigned ? TR::InstOpCode::lhz : TR::InstOpCode::lha;
      else
         return TR::InstOpCode::sth;
   case TR::Int32:
      if (returnLoad)
         return TR::InstOpCode::lwz;
      else
         return TR::InstOpCode::stw;
   case TR::Int64:
      return returnLoad ? TR::InstOpCode::ld : TR::InstOpCode::std;
   case TR::Float:
      return returnLoad ? TR::InstOpCode::lfs : TR::InstOpCode::stfs;
   case TR::Double:
      return returnLoad ? TR::InstOpCode::lfd : TR::InstOpCode::stfd;
   case TR::Address:
      if (returnLoad)
         return elementSize == 8 ? TR::InstOpCode::ld : TR::InstOpCode::lwz;
      else
         return elementSize == 8 ? TR::InstOpCode::std : TR::InstOpCode::stw;
   default:
      TR_ASSERT(false, "Unexpected array data type");
      return TR::InstOpCode::bad;
      }
   }

static void genDecompressPointer(TR::CodeGenerator *cg, TR::Node *node, TR::Register *ptrReg, TR::Register *condReg = NULL, bool nullCheck = true)
   {
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->comp()->fe());
   int32_t shiftAmount = TR::Compiler->om.compressedReferenceShift();

   if (shiftAmount != 0)
      generateShiftLeftImmediateLong(cg, node, ptrReg, ptrReg, shiftAmount);
   }

static TR::Register *addConstantToLongWithTempReg(TR::Node * node, TR::Register *srcReg, int64_t value, TR::Register *trgReg, TR::Register *tempReg, TR::CodeGenerator *cg)
   {
   if (!trgReg)
      trgReg = cg->allocateRegister();

   if ((int16_t) value == value)
      {
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi2, node, trgReg, srcReg, value);
      }
   // NOTE: the following only works if the second add's immediate is not sign extended
   else if (((int32_t) value == value) && ((value & 0x8000) == 0))
      {
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addis, node, trgReg, srcReg, value >> 16);
      if (value & 0xffff)
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi2, node, trgReg, trgReg, LO_VALUE(value));
      }
   else
      {
      loadConstant(cg, node, value, tempReg);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, trgReg, srcReg, tempReg);
      }

   return trgReg;
   }

static void genDecompressPointerWithTempReg(TR::CodeGenerator *cg, TR::Node *node, TR::Register *ptrReg, TR::Register *tempReg, TR::Register *condReg = NULL, bool nullCheck = true)
   {
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->comp()->fe());
   int32_t shiftAmount = TR::Compiler->om.compressedReferenceShift();

   if (shiftAmount != 0)
      generateShiftLeftImmediateLong(cg, node, ptrReg, ptrReg, shiftAmount);
   }

static TR::Register *genDecompressPointerNonNull2Regs(TR::CodeGenerator *cg, TR::Node *node, TR::Register *ptrReg, TR::Register *targetReg)
   {
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->comp()->fe());
   int32_t shiftAmount = TR::Compiler->om.compressedReferenceShift();

   if (shiftAmount != 0)
      {
      generateShiftLeftImmediateLong(cg, node, targetReg, ptrReg, shiftAmount);
      return targetReg;
      }
   else
      {
      return ptrReg;
      }
   }

static TR::Register *genDecompressPointerNonNull2RegsWithTempReg(TR::CodeGenerator *cg, TR::Node *node, TR::Register *ptrReg, TR::Register *targetReg, TR::Register *tempReg)
   {
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->comp()->fe());
   int32_t shiftAmount = TR::Compiler->om.compressedReferenceShift();

   if (shiftAmount != 0)
      {
      generateShiftLeftImmediateLong(cg, node, targetReg, ptrReg, shiftAmount);
      return targetReg;
      }
   else
      {
      return ptrReg;
      }
   }

static void genCompressPointerWithTempReg(TR::CodeGenerator *cg, TR::Node *node, TR::Register *tempReg, TR::Register *ptrReg, TR::Register *condReg = NULL, bool nullCheck = true)
   {
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->comp()->fe());
   int32_t shiftAmount = TR::Compiler->om.compressedReferenceShift();

   if (shiftAmount != 0)
      {
      generateShiftRightLogicalImmediateLong(cg, node, tempReg, ptrReg, shiftAmount);
      }
   }

static void genCompressPointer(TR::CodeGenerator *cg, TR::Node *node, TR::Register *ptrReg, TR::Register *condReg = NULL, bool nullCheck = true)
   {
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->comp()->fe());
   int32_t shiftAmount = TR::Compiler->om.compressedReferenceShift();

   if (shiftAmount != 0)
      {
      generateShiftRightLogicalImmediateLong(cg, node, ptrReg, ptrReg, shiftAmount);
      }
   }

static TR::Register *genCompressPointerNonNull2RegsWithTempReg(TR::CodeGenerator *cg, TR::Node *node, TR::Register *ptrReg, TR::Register *targetReg, TR::Register *tempReg)
   {
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->comp()->fe());
   int32_t shiftAmount = TR::Compiler->om.compressedReferenceShift();

   if (shiftAmount != 0)
      {
      generateShiftRightLogicalImmediateLong(cg, node, targetReg, ptrReg, shiftAmount);
      return targetReg;
      }
   else
      return ptrReg;
   }

static TR::Register *genCompressPointerNonNull2Regs(TR::CodeGenerator *cg, TR::Node *node, TR::Register *ptrReg, TR::Register *targetReg)
   {
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->comp()->fe());
   int32_t shiftAmount = TR::Compiler->om.compressedReferenceShift();

   if (shiftAmount != 0)
      {
      generateShiftRightLogicalImmediateLong(cg, node, targetReg, ptrReg, shiftAmount);
      return targetReg;
      }
   else
      return ptrReg;
   }

// Handles both BNDCHKwithSpineCHK and SpineCHK nodes.
//
TR::Register *J9::Power::TreeEvaluator::BNDCHKwithSpineCHKEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());
   bool noTrap = !cg->getHasResumableTrapHandler();
   bool needsBoundCheck = node->getOpCodeValue() == TR::BNDCHKwithSpineCHK;
   bool needsBoundCheckOOL;

   TR_PPCScratchRegisterManager *srm = cg->generateScratchRegisterManager();

   TR::Node *loadOrStoreChild = node->getFirstChild();
   TR::Node *baseArrayChild = node->getSecondChild();
   TR::Node *arrayLengthChild;
   TR::Node *indexChild;

   if (needsBoundCheck)
      {
      arrayLengthChild = node->getChild(2);
      indexChild = node->getChild(3);
      }
   else
      indexChild = node->getChild(2);

   TR::Register *baseArrayReg = cg->evaluate(baseArrayChild);
   TR::Register *indexReg;
   TR::Register *loadOrStoreReg;
   TR::Register *arrayLengthReg;

   // If the index is too large to be an immediate load it in a register
   if (!indexChild->getOpCode().isLoadConst() || (indexChild->getInt() > UPPER_IMMED || indexChild->getInt() < 0))
      indexReg = cg->evaluate(indexChild);
   else
      indexReg = NULL;

   // For primitive stores anchored under the check node, we must evaluate the source node
   // before the bound check branch so that its available to the snippet.
   //
   if (loadOrStoreChild->getOpCode().isStore() && !loadOrStoreChild->getRegister())
      {
      TR::Node *valueChild = loadOrStoreChild->getSecondChild();
      cg->evaluate(valueChild);
      }

   // Evaluate any escaping nodes before the OOL branch since they won't be evaluated in the OOL path.
   preEvaluateEscapingNodesForSpineCheck(node, cg);

   // Label to the OOL code that will perform the load/store/agen for discontiguous arrays (and the bound check if needed).
   TR::LabelSymbol *discontiguousArrayLabel = generateLabelSymbol(cg);

   // Label back to main-line that the OOL code will branch to when done.
   TR::LabelSymbol *doneLabel = generateLabelSymbol(cg);
   doneLabel->setEndInternalControlFlow();

   TR_PPCOutOfLineCodeSection *discontiguousArrayOOL = new (cg->trHeapMemory()) TR_PPCOutOfLineCodeSection(discontiguousArrayLabel, doneLabel, cg);
   cg->getPPCOutOfLineCodeSectionList().push_front(discontiguousArrayOOL);

   TR::Instruction *OOLBranchInstr;

   if (needsBoundCheck)
      {
      TR_ASSERT(arrayLengthChild, "Expecting to have an array length child for BNDCHKwithSpineCHK node");
      TR_ASSERT(
            arrayLengthChild->getOpCode().isConversion() || arrayLengthChild->getOpCodeValue() == TR::iloadi || arrayLengthChild->getOpCodeValue() == TR::iload
                  || arrayLengthChild->getOpCodeValue() == TR::iRegLoad || arrayLengthChild->getOpCode().isLoadConst(),
            "Expecting array length child under BNDCHKwithSpineCHK to be a conversion, iloadi, iload, iRegLoad or iconst");

      TR::Register *condReg = srm->findOrCreateScratchRegister(TR_CCR);

      arrayLengthReg = arrayLengthChild->getRegister();

      if (arrayLengthReg)
         {
         OOLBranchInstr = genSpineCheck(cg, node, baseArrayReg, arrayLengthReg, condReg, discontiguousArrayLabel);
         needsBoundCheckOOL = true;
         genBoundCheck(cg, node, indexReg, indexChild->getInt(), arrayLengthReg, arrayLengthChild->getInt(), condReg, noTrap);
         }
      else if (arrayLengthChild->getOpCode().isLoadConst())
         {
         // If the constant arraylength is non-zero then it will pass the spine check (hence its
         // a contiguous array) and the BNDCHK can be done inline with no OOL path.
         //
         // If the constant arraylength is zero then we will always go OOL.
         //
         // TODO: in the future there shouldn't be an OOL path because any valid access must be
         //       on a discontiguous array.
         //
         if (arrayLengthChild->getInt() != 0)
            {
            // The array must be contiguous.
            //

            // If the array length is too large to be an immediate load it in a register for the bound check
            if (arrayLengthChild->getInt() > UPPER_IMMED || arrayLengthChild->getInt() < 0)
               arrayLengthReg = cg->evaluate(arrayLengthChild);

            // Do the bound check first.
            genBoundCheck(cg, node, indexReg, indexChild->getInt(), arrayLengthReg, arrayLengthChild->getInt(), condReg, noTrap);
            needsBoundCheckOOL = false;
            TR::Register *scratchArrayLengthReg = srm->findOrCreateScratchRegister();
            OOLBranchInstr = genSpineCheck(cg, node, baseArrayReg, scratchArrayLengthReg, condReg, discontiguousArrayLabel);
            srm->reclaimScratchRegister(scratchArrayLengthReg);
            }
         else
            {
            // Zero length array or discontiguous array.  Unconditionally branch to the OOL path
            // to find out which.
            //
            OOLBranchInstr = generateLabelInstruction(cg, TR::InstOpCode::b, node, discontiguousArrayLabel);
            needsBoundCheckOOL = true;
            }
         }
      else
         {
         // Load the contiguous array length.
         arrayLengthReg = cg->evaluate(arrayLengthChild);
         // If the array length is 0, this is a discontiguous array and the bound check will be handled OOL.
         OOLBranchInstr = genSpineCheck(cg, node, arrayLengthReg, condReg, discontiguousArrayLabel);
         needsBoundCheckOOL = true;
         // Do the bound check using the contiguous array length.
         genBoundCheck(cg, node, indexReg, indexChild->getInt(), arrayLengthReg, arrayLengthChild->getInt(), condReg, noTrap);
         }

      srm->reclaimScratchRegister(condReg);
      cg->decReferenceCount(arrayLengthChild);
      }
   else
      {
      // Spine check only; load the contiguous length, check for 0, branch OOL if discontiguous.
      needsBoundCheckOOL = false;

      arrayLengthReg = srm->findOrCreateScratchRegister();

      TR::Register *condReg = srm->findOrCreateScratchRegister(TR_CCR);

      OOLBranchInstr = genSpineCheck(cg, node, baseArrayReg, arrayLengthReg, condReg, discontiguousArrayLabel);

      srm->reclaimScratchRegister(arrayLengthReg);
      srm->reclaimScratchRegister(condReg);
      }

   // For reference stores, only evaluate the array element address because the store cannot
   // happen here (it must be done via the array store check).
   //
   // For primitive stores, evaluate them now.
   // For loads, evaluate them now.
   // For address calculations (aladd/aiadd), evaluate them now.
   //
   bool doLoadOrStore;
   bool doLoadDecompress = false;
   bool doAddressComputation;

   if (loadOrStoreChild->getOpCode().isStore() && loadOrStoreChild->getReferenceCount() > 1)
      {
      TR_ASSERT(loadOrStoreChild->getOpCode().isWrtBar(), "Opcode must be wrtbar");
      loadOrStoreReg = cg->evaluate(loadOrStoreChild->getFirstChild());
      cg->decReferenceCount(loadOrStoreChild->getFirstChild());
      doLoadOrStore = false;
      doAddressComputation = true;
      }
   else
      {
      // If it's a store and not commoned then it must be a primitive store.
      // If it's an address load it may need decompression in the OOL path.

      // Top-level check whether a decompression sequence is necessary, because the first child
      // may have been created by a PRE temp.
      //
      if ((loadOrStoreChild->getOpCodeValue() == TR::aload || loadOrStoreChild->getOpCodeValue() == TR::aRegLoad) && node->isSpineCheckWithArrayElementChild() && comp->target().is64Bit()
            && comp->useCompressedPointers())
         {
         doLoadDecompress = true;
         }

      TR::Node *actualLoadOrStoreChild = loadOrStoreChild;
      while (actualLoadOrStoreChild->getOpCode().isConversion() || actualLoadOrStoreChild->containsCompressionSequence())
         {
         if (actualLoadOrStoreChild->containsCompressionSequence())
            doLoadDecompress = true;
         actualLoadOrStoreChild = actualLoadOrStoreChild->getFirstChild();
         }

      doLoadOrStore = actualLoadOrStoreChild->getOpCode().hasSymbolReference()
            && (actualLoadOrStoreChild->getSymbolReference()->getSymbol()->isArrayShadowSymbol()
                  || actualLoadOrStoreChild->getSymbolReference()->getSymbol()->isArrayletShadowSymbol()) && node->isSpineCheckWithArrayElementChild();

      // If the 1st child is not a load/store/aladd/aiadd it's probably a nop (e.g. const) at this point due to commoning
      //
      doAddressComputation = !doLoadOrStore && actualLoadOrStoreChild->getOpCode().isArrayRef() && !node->isSpineCheckWithArrayElementChild();

      if (doLoadOrStore || doAddressComputation || !loadOrStoreChild->getOpCode().isLoadConst())
         loadOrStoreReg = cg->evaluate(loadOrStoreChild);
      else
         loadOrStoreReg = NULL;
      }

   if (noTrap)
      {
      TR::RegisterDependencyConditions *mainlineDeps = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, 1, cg->trMemory());
      // Trampoline to bound check fail kills gr11
      // TODO: Try to use this as the arraylet reg
      TR::Register *gr11 = cg->allocateRegister();
      mainlineDeps->addPostCondition(gr11, TR::RealRegister::gr11);
      cg->stopUsingRegister(gr11);

      generateLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel);
      TR::LabelSymbol *doneMainlineLabel = generateLabelSymbol(cg);
      generateDepLabelInstruction(cg, TR::InstOpCode::label, node, doneMainlineLabel, mainlineDeps);
      }
   else
      generateLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel);

   // OOL begin.
   //
   discontiguousArrayOOL->swapInstructionListsWithCompilation();
      {
      TR::Instruction *OOLLabelInstr = generateLabelInstruction(cg, TR::InstOpCode::label, node, discontiguousArrayLabel);
      // XXX: Temporary fix, OOL instruction stream does not pick up live locals or monitors correctly.
      TR_ASSERT(!OOLLabelInstr->getLiveLocals() && !OOLLabelInstr->getLiveMonitors(), "Expecting first OOL instruction to not have live locals/monitors info");
      OOLLabelInstr->setLiveLocals(OOLBranchInstr->getLiveLocals());
      OOLLabelInstr->setLiveMonitors(OOLBranchInstr->getLiveMonitors());

      if (needsBoundCheckOOL)
         {
         TR_ASSERT(needsBoundCheck, "Inconsistent state, needs bound check OOL but doesn't need bound check");

         TR::MemoryReference *discontiguousArraySizeMR = TR::MemoryReference::createWithDisplacement(cg, baseArrayReg, fej9->getOffsetOfDiscontiguousArraySizeField(), 4);
         TR::Register *arrayLengthScratchReg = srm->findOrCreateScratchRegister();

         generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, arrayLengthScratchReg, discontiguousArraySizeMR);

         TR::Register *condReg = srm->findOrCreateScratchRegister(TR_CCR);

         // Do the bound check using the discontiguous array length.
         genBoundCheck(cg, node, indexReg, indexChild->getInt(), arrayLengthScratchReg, arrayLengthChild->getInt(), condReg, noTrap);

         srm->reclaimScratchRegister(condReg);
         srm->reclaimScratchRegister(arrayLengthScratchReg);
         }

      TR_ASSERT(!(doLoadOrStore && doAddressComputation), "Unexpected condition");

      TR::Register *arrayletReg;
      TR::DataType dt = loadOrStoreChild->getDataType();

      if (doLoadOrStore || doAddressComputation)
         {
         arrayletReg = doAddressComputation ? loadOrStoreReg : /* Needs to be in !gr0 in this case */cg->allocateRegister();

         // Generate the base+offset address pair into the arraylet.
         //
         int32_t elementSize = dt == TR::Address ? TR::Compiler->om.sizeofReferenceField() : TR::Symbol::convertTypeToSize(dt);
         TR::Register *arrayletOffsetReg;
         int32_t arrayletOffsetVal;

         if (indexReg)
            arrayletOffsetReg = srm->findOrCreateScratchRegister();

         genArrayletAccessAddr(cg, node, elementSize, baseArrayReg, indexReg, indexChild->getInt(), arrayletReg, arrayletOffsetReg, arrayletOffsetVal);

         // Decompress the arraylet pointer if necessary.
         genDecompressPointer(cg, node, arrayletReg);

         if (doLoadOrStore)
            {
            // Generate the load or store.
            //
            if (loadOrStoreChild->getOpCode().isStore())
               {
               bool isUnsigned = loadOrStoreChild->getOpCode().isUnsigned();
               TR::InstOpCode::Mnemonic storeOp = getLoadOrStoreFromDataType(cg, dt, elementSize, isUnsigned, false);

               if (storeOp == TR::InstOpCode::std && comp->target().is32Bit())
                  {
                  // Store using consecutive stws.
                  TR_ASSERT(elementSize == 8 && loadOrStoreChild->getSecondChild()->getRegister()->getRegisterPair(), "Expecting 64-bit store value to be in a register pair");
                  if (indexReg)
                     {
                     TR::MemoryReference *arrayletMR = TR::MemoryReference::createWithIndexReg(cg, arrayletReg, arrayletOffsetReg, 4);
                     generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, arrayletMR, loadOrStoreChild->getSecondChild()->getRegister()->getHighOrder());

                     generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, arrayletOffsetReg, arrayletOffsetReg, 4);

                     TR::MemoryReference *arrayletMR2 = TR::MemoryReference::createWithIndexReg(cg, arrayletReg, arrayletOffsetReg, 4);
                     generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, arrayletMR2, loadOrStoreChild->getSecondChild()->getRegister()->getLowOrder());
                     }
                  else
                     {
                     TR::MemoryReference *arrayletMR = TR::MemoryReference::createWithDisplacement(cg, arrayletReg, arrayletOffsetVal, 4);
                     TR::MemoryReference *arrayletMR2 = TR::MemoryReference::createWithDisplacement(cg, arrayletReg, arrayletOffsetVal + 4, 4);
                     generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, arrayletMR, loadOrStoreChild->getSecondChild()->getRegister()->getHighOrder());
                     generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, arrayletMR2, loadOrStoreChild->getSecondChild()->getRegister()->getLowOrder());
                     }
                  }
               else
                  {
                  TR::MemoryReference *arrayletMR =
                        indexReg ? TR::MemoryReference::createWithIndexReg(cg, arrayletReg, arrayletOffsetReg, elementSize) :
                                   TR::MemoryReference::createWithDisplacement(cg, arrayletReg, arrayletOffsetVal, elementSize);
                  generateMemSrc1Instruction(cg, storeOp, node, arrayletMR, loadOrStoreChild->getSecondChild()->getRegister());
                  }
               }
            else
               {
               TR_ASSERT(loadOrStoreChild->getOpCode().isConversion() || loadOrStoreChild->getOpCode().isLoad(), "Unexpected op");

               bool isUnsigned = loadOrStoreChild->getOpCode().isUnsigned();
               TR::InstOpCode::Mnemonic loadOp = getLoadOrStoreFromDataType(cg, dt, elementSize, isUnsigned, true);

               if (loadOp == TR::InstOpCode::ld && comp->target().is32Bit())
                  {
                  // Load using consecutive lwzs.
                  TR_ASSERT(elementSize == 8 && loadOrStoreReg->getRegisterPair(), "Expecting 64-bit load value to be in a register pair");
                  if (indexReg)
                     {
                     TR::MemoryReference *arrayletMR = TR::MemoryReference::createWithIndexReg(cg, arrayletReg, arrayletOffsetReg, 4);
                     generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, loadOrStoreReg->getHighOrder(), arrayletMR);

                     generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, arrayletOffsetReg, arrayletOffsetReg, 4);

                     TR::MemoryReference *arrayletMR2 = TR::MemoryReference::createWithIndexReg(cg, arrayletReg, arrayletOffsetReg, 4);
                     generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, loadOrStoreReg->getLowOrder(), arrayletMR2);
                     }
                  else
                     {
                     TR::MemoryReference *arrayletMR = TR::MemoryReference::createWithDisplacement(cg, arrayletReg, arrayletOffsetVal, 4);
                     TR::MemoryReference *arrayletMR2 = TR::MemoryReference::createWithDisplacement(cg, arrayletReg, arrayletOffsetVal + 4, 4);
                     generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, loadOrStoreReg->getHighOrder(), arrayletMR);
                     generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, loadOrStoreReg->getLowOrder(), arrayletMR2);
                     }
                  }
               else
                  {
                  TR::MemoryReference *arrayletMR =
                        indexReg ? TR::MemoryReference::createWithIndexReg(cg, arrayletReg, arrayletOffsetReg, elementSize) :
                                   TR::MemoryReference::createWithDisplacement(cg, arrayletReg, arrayletOffsetVal, elementSize);
                  generateTrg1MemInstruction(cg, loadOp, node, loadOrStoreReg, arrayletMR);
                  }

               if (doLoadDecompress)
                  {
                  TR_ASSERT(dt == TR::Address, "Expecting loads with decompression trees to have data type TR::Address");
                  genDecompressPointer(cg, node, loadOrStoreReg);
                  }
               }

            cg->stopUsingRegister(arrayletReg);
            }
         else
            {
            if (indexReg)
               generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, loadOrStoreReg, loadOrStoreReg, arrayletOffsetReg);
            else
               addConstantToInteger(node, loadOrStoreReg, loadOrStoreReg, arrayletOffsetVal, cg);
            }

         if (indexReg)
            srm->reclaimScratchRegister(arrayletOffsetReg);
         }

      const uint32_t numOOLDeps = 1 + (doLoadOrStore ? 1 : 0) + (needsBoundCheck && arrayLengthReg ? 1 : 0) + (loadOrStoreReg ? (loadOrStoreReg->getRegisterPair() ? 2 : 1) : 0)
            + (indexReg ? 1 : 0) + srm->numAvailableRegisters();
      TR::RegisterDependencyConditions *OOLDeps = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, numOOLDeps, cg->trMemory());
      OOLDeps->addPostCondition(baseArrayReg, TR::RealRegister::NoReg);
      TR_ASSERT(OOLDeps->getPostConditions()->getRegisterDependency(0)->getRegister() == baseArrayReg, "Unexpected register");
      OOLDeps->getPostConditions()->getRegisterDependency(0)->setExcludeGPR0();
      if (doLoadOrStore)
         {
         OOLDeps->addPostCondition(arrayletReg, TR::RealRegister::NoReg);
         TR_ASSERT(OOLDeps->getPostConditions()->getRegisterDependency(1)->getRegister() == arrayletReg, "Unexpected register");
         OOLDeps->getPostConditions()->getRegisterDependency(1)->setExcludeGPR0();
         }
      if (indexReg)
         OOLDeps->addPostCondition(indexReg, TR::RealRegister::NoReg);
      if (loadOrStoreReg)
         {
         if (loadOrStoreReg->getRegisterPair())
            {
            TR_ASSERT(dt == TR::Int64 && comp->target().is32Bit(), "Unexpected register pair");
            OOLDeps->addPostCondition(loadOrStoreReg->getHighOrder(), TR::RealRegister::NoReg);
            OOLDeps->addPostCondition(loadOrStoreReg->getLowOrder(), TR::RealRegister::NoReg);
            }
         else
            OOLDeps->addPostCondition(loadOrStoreReg, TR::RealRegister::NoReg);
         }
      if (needsBoundCheck && arrayLengthReg)
         OOLDeps->addPostCondition(arrayLengthReg, TR::RealRegister::NoReg);
      srm->addScratchRegistersToDependencyList(OOLDeps);

      srm->stopUsingRegisters();

      TR::LabelSymbol *doneOOLLabel = generateLabelSymbol(cg);
      generateDepLabelInstruction(cg, TR::InstOpCode::label, node, doneOOLLabel, OOLDeps);
      generateLabelInstruction(cg, TR::InstOpCode::b, node, doneLabel);
      }
   discontiguousArrayOOL->swapInstructionListsWithCompilation();
   //
   // OOL end.

   cg->decReferenceCount(loadOrStoreChild);
   cg->decReferenceCount(baseArrayChild);
   cg->decReferenceCount(indexChild);

   return NULL;
   }

static void VMoutlinedHelperArrayStoreCHKEvaluator(TR::Node *node, TR::Register *srcReg, TR::Register *dstReg, bool srcIsNonNull, TR::CodeGenerator *cg)
   {
   TR::Compilation * comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());
   TR::CodeCache *codeCache = cg->getCodeCache();
   TR::LabelSymbol *doneArrayStoreCHKLabel = generateLabelSymbol(cg);
   TR::Register *rootClassReg = cg->allocateRegister();
   TR::Register *scratchReg = cg->allocateRegister();
   TR::Register *srcNullCondReg;

   TR_PPCScratchRegisterDependencyConditions deps;

   if (!srcIsNonNull)
      {
      srcNullCondReg = cg->allocateRegister(TR_CCR);
      generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpli, node, srcNullCondReg, srcReg, NULLVALUE);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneArrayStoreCHKLabel, srcNullCondReg);
      }

   deps.addDependency(cg, dstReg, TR::RealRegister::gr3);
   deps.addDependency(cg, srcReg, TR::RealRegister::gr4);
   // Clobbered by the helper
   deps.addDependency(cg, NULL, TR::RealRegister::gr5);
   deps.addDependency(cg, NULL, TR::RealRegister::gr6);
   deps.addDependency(cg, scratchReg, TR::RealRegister::gr7);
   deps.addDependency(cg, rootClassReg, TR::RealRegister::gr11);

   TR_OpaqueClassBlock *rootClass = fej9->getSystemClassFromClassName("java/lang/Object", 16);
   loadAddressConstant(cg, comp->compileRelocatableCode(), node, (intptr_t) rootClass, rootClassReg);

   TR_CCPreLoadedCode helper = TR_arrayStoreCHK;
   uintptr_t helperAddr = (uintptr_t) codeCache->getCCPreLoadedCodeAddress(helper, cg);
   TR::SymbolReference *symRef = comp->getSymRefTab()->findOrCreatePerCodeCacheHelperSymbolRef(helper, helperAddr);

   TR::Instruction *gcPoint = generateDepImmSymInstruction(cg, TR::InstOpCode::bl, node, helperAddr, new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, 0, cg->trMemory()),
         symRef);
   gcPoint->PPCNeedsGCMap(0xFFFFFFFF);

   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, doneArrayStoreCHKLabel, TR_PPCScratchRegisterDependencyConditions::createDependencyConditions(cg, NULL, &deps));

   cg->stopUsingRegister(rootClassReg);
   cg->stopUsingRegister(scratchReg);

   if (!srcIsNonNull)
      cg->stopUsingRegister(srcNullCondReg);
   }

TR::Register *outlinedHelperArrayStoreCHKEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR::Node *srcNode = node->getFirstChild()->getSecondChild();
   TR::Node *dstNode = node->getFirstChild()->getThirdChild();
   TR::Register *srcReg = cg->evaluate(srcNode);
   TR::Register *dstReg = cg->evaluate(dstNode);
   TR::Compilation* comp = cg->comp();

   if (!srcNode->isNull())
      {
      VMoutlinedHelperArrayStoreCHKEvaluator(node, srcReg, dstReg, srcNode->isNonNull(), cg);
      }

   cg->evaluate(node->getFirstChild());
   cg->decReferenceCount(node->getFirstChild());

   return NULL;
   }

TR::Instruction *J9::Power::TreeEvaluator::generateVFTMaskInstruction(TR::CodeGenerator *cg, TR::Node *node, TR::Register *dstReg, TR::Register *srcReg, TR::Instruction *preced)
   {
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());
   uintptr_t mask = TR::Compiler->om.maskOfObjectVftField();
   bool isCompressed = TR::Compiler->om.compressObjectReferences();
   if (~mask == 0)
      {
      // no mask instruction required
      return preced;
      }
   else
      {
      TR::InstOpCode::Mnemonic op = (cg->comp()->target().is64Bit() && !isCompressed) ? TR::InstOpCode::rldicr : TR::InstOpCode::rlwinm;
      return generateTrg1Src1Imm2Instruction(cg, op, node, dstReg, srcReg, 0, mask, preced);
      }
   }

TR::Instruction *J9::Power::TreeEvaluator::generateVFTMaskInstruction(TR::CodeGenerator *cg, TR::Node *node, TR::Register *reg, TR::Instruction *preced)
   {
   return TR::TreeEvaluator::generateVFTMaskInstruction(cg, node, reg, reg, preced);
   }

static TR::Instruction *genTestIsSuper(TR::Node *node, TR::Register *objClassReg, TR::Register *castClassReg, TR::Register *crReg, TR::Register *scratch1Reg,
      TR::Register *scratch2Reg, int32_t castClassDepth, TR::LabelSymbol *failLabel, TR::Instruction *cursor, TR::CodeGenerator *cg, bool depthInReg2 = false)
   {
   TR::Compilation *comp = cg->comp();
   int32_t superClassOffset = castClassDepth * TR::Compiler->om.sizeofReferenceAddress();
   bool outOfBound = (!depthInReg2 && (superClassOffset > UPPER_IMMED || superClassOffset < LOWER_IMMED)) ? true : false;
#ifdef OMR_GC_COMPRESSED_POINTERS
   // objClassReg contains the class offset so we may need to generate code
   // to convert from class offset to real J9Class pointer
#endif
   cursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, scratch1Reg,
         TR::MemoryReference::createWithDisplacement(cg, objClassReg, offsetof(J9Class, classDepthAndFlags), TR::Compiler->om.sizeofReferenceAddress()), cursor);
   if (outOfBound)
      {
      cursor = loadConstant(cg, node, castClassDepth, scratch2Reg, cursor);
      }
   cursor = generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, scratch1Reg, scratch1Reg, 0, J9AccClassDepthMask, cursor);

   if (outOfBound || depthInReg2)
      {
      cursor = generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp4, node, crReg, scratch1Reg, scratch2Reg, cursor);
      }
   else
      {
      cursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, crReg, scratch1Reg, castClassDepth, cursor);
      }
   cursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::ble, node, failLabel, crReg, cursor);

   if (outOfBound || depthInReg2)
      {
      if (comp->target().is64Bit())
         cursor = generateShiftLeftImmediateLong(cg, node, scratch2Reg, scratch2Reg, 3, cursor);
      else
         cursor = generateShiftLeftImmediate(cg, node, scratch2Reg, scratch2Reg, 2, cursor);
      }
#ifdef OMR_GC_COMPRESSED_POINTERS
   // objClassReg contains the class offset so we may need to generate code
   // to convert from class offset to real J9Class pointer
#endif
   cursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, scratch1Reg,
         TR::MemoryReference::createWithDisplacement(cg, objClassReg, offsetof(J9Class, superclasses), TR::Compiler->om.sizeofReferenceAddress()), cursor);

   if (outOfBound || depthInReg2)
      {
      cursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_loadx, node, scratch1Reg, TR::MemoryReference::createWithIndexReg(cg, scratch1Reg, scratch2Reg, TR::Compiler->om.sizeofReferenceAddress()),
            cursor);
      }
   else
      {
      cursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, scratch1Reg,
            TR::MemoryReference::createWithDisplacement(cg, scratch1Reg, superClassOffset, TR::Compiler->om.sizeofReferenceAddress()), cursor);
      }
#ifdef OMR_GC_COMPRESSED_POINTERS
   // castClassReg has a class offset and scratch1Reg contains a J9Class pointer
   // May need to convert the J9Class pointer to a class offset
#endif
   cursor = generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmp, node, crReg, scratch1Reg, castClassReg, cursor);
   return (cursor);
   }

static void VMarrayStoreCHKEvaluator(TR::Node *node, TR::Register *src, TR::Register *dst, TR::Register *t1Reg, TR::Register *t2Reg, TR::Register *t3Reg, TR::Register *t4Reg,
      TR::Register *cndReg, TR::LabelSymbol *toWB, TR::LabelSymbol *fLabel, TR::CodeGenerator *cg)
   {
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());

   generateLoadJ9Class(node, t1Reg, dst, cg);
   generateLoadJ9Class(node, t2Reg, src, cg);

   generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, t1Reg,
         TR::MemoryReference::createWithDisplacement(cg, t1Reg, (int32_t)offsetof(J9ArrayClass, componentType), TR::Compiler->om.sizeofReferenceAddress()));

   if (!comp->compileRelocatableCode() || comp->getOption(TR_UseSymbolValidationManager))
      {
      TR_OpaqueClassBlock *rootClass = fej9->getSystemClassFromClassName("java/lang/Object", 16);

      if (comp->compileRelocatableCode())
         {
         TR::StaticSymbol *sym = TR::StaticSymbol::create(comp->trHeapMemory(), TR::Address);
         sym->setStaticAddress(rootClass);
         sym->setClassObject();

         loadAddressConstant(cg, true, node, (intptr_t) new (comp->trHeapMemory()) TR::SymbolReference(comp->getSymRefTab(), sym), t3Reg, NULL, false, TR_ClassAddress);
         }
      else
         {
         loadAddressConstant(cg, false, node, (intptr_t) rootClass, t3Reg);
         }

      generateTrg1Src2Instruction(cg, TR::InstOpCode::Op_cmpl, node, cndReg, t1Reg, t3Reg);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, toWB, cndReg);
      }

   generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, t4Reg,
         TR::MemoryReference::createWithDisplacement(cg, t2Reg, (int32_t) offsetof(J9Class, castClassCache), TR::Compiler->om.sizeofReferenceAddress()));

   generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, cndReg, t1Reg, t2Reg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, toWB, cndReg);

   generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, cndReg, t1Reg, t4Reg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, toWB, cndReg);

   generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, cndReg, t1Reg, t3Reg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, toWB, cndReg);

   if ((!comp->getOption(TR_DisableArrayStoreCheckOpts)) && node->getArrayComponentClassInNode())
      {
      TR_OpaqueClassBlock *castClass = (TR_OpaqueClassBlock *) node->getArrayComponentClassInNode();
      loadAddressConstant(cg, comp->compileRelocatableCode(), node, (intptr_t) castClass, t3Reg);
      generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, cndReg, t1Reg, t3Reg);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, toWB, cndReg);
      }

#ifdef OMR_GC_COMPRESSED_POINTERS
   // For the following two instructions
   // we may need to convert the class offset from t1Reg into J9Class
#endif
   generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, t3Reg, TR::MemoryReference::createWithDisplacement(cg, t1Reg, (int32_t) offsetof(J9Class, romClass), TR::Compiler->om.sizeofReferenceAddress()));
   generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, t4Reg,
         TR::MemoryReference::createWithDisplacement(cg, t1Reg, (int32_t) offsetof(J9Class, classDepthAndFlags), TR::Compiler->om.sizeofReferenceAddress()));
   generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, t3Reg, TR::MemoryReference::createWithDisplacement(cg, t3Reg, (int32_t) offsetof(J9ROMClass, modifiers), 4));
   generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, t4Reg, t4Reg, 0, J9AccClassDepthMask);
   generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, t3Reg, t3Reg, 31, 0xFFFFFFFF);
   TR_ASSERT(J9AccClassArray == (1 << 16) && J9AccInterface == (1 << 9), "Verify code sequence is still right ...");
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, t3Reg, t3Reg, cndReg, (J9AccClassArray | J9AccInterface) >> 1);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, fLabel, cndReg);
   genTestIsSuper(node, t2Reg, t1Reg, cndReg, t3Reg, t4Reg, 0, fLabel, cg->getAppendInstruction(), cg, true);

   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, fLabel, cndReg);
   }

TR::Register *J9::Power::TreeEvaluator::ArrayStoreCHKEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());
   static bool oldArrayStoreCHK = feGetEnv("TR_newArrayStoreCheck") == NULL;
   if (!comp->compileRelocatableCode() && !comp->getOptions()->realTimeGC() && !comp->useCompressedPointers() && !oldArrayStoreCHK)
      return outlinedHelperArrayStoreCHKEvaluator(node, cg);

   // Note: we take advantage of the register conventions of the helpers by limiting register usages on
   //       the fast-path (most likely 4 registers; at most, 6 registers)

   TR::Node * firstChild = node->getFirstChild();

   TR::Node *sourceChild = firstChild->getSecondChild();
   TR::Node *destinationChild = firstChild->getChild(2);

   if (comp->useCompressedPointers() && firstChild->getOpCode().isIndirect())
      {
      while ((sourceChild->getNumChildren() > 0) && (sourceChild->getOpCodeValue() != TR::a2l))
         sourceChild = sourceChild->getFirstChild();
      if (sourceChild->getOpCodeValue() == TR::a2l)
         sourceChild = sourceChild->getFirstChild();
      }

   // Since ArrayStoreCHK doesn't have the shape of the corresponding helper call we have to create this tree
   // so we can have it evaluated out of line
   TR::Node *helperCallNode = TR::Node::createWithSymRef(node, TR::call, 2, node->getSymbolReference());
   helperCallNode->setAndIncChild(0, sourceChild);
   helperCallNode->setAndIncChild(1, destinationChild);
   if (comp->getOption(TR_TraceCG))
      {
      traceMsg(comp, "%s: Creating and evaluating the following tree to generate the necessary helper call for this node\n", node->getOpCode().getName());
      cg->getDebug()->print(comp->getOutFile(), helperCallNode);
      }

   TR::Register *srcReg, *dstReg;
   TR::Register *condReg, *temp1Reg, *temp2Reg, *temp3Reg, *temp4Reg;
   TR::LabelSymbol *wbLabel, *startLabel;
   TR::RegisterDependencyConditions *conditions;

   wbLabel = generateLabelSymbol(cg);

   dstReg = cg->evaluate(destinationChild);
   srcReg = cg->evaluate(sourceChild);

   conditions = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(9, 9, cg->trMemory());
   temp1Reg = cg->allocateRegister();
   temp2Reg = cg->allocateRegister();
   temp3Reg = cg->allocateRegister();
   temp4Reg = cg->allocateRegister();
   condReg = cg->allocateRegister(TR_CCR);

   TR::addDependency(conditions, dstReg, TR::RealRegister::NoReg, TR_GPR, cg);
   conditions->getPreConditions()->getRegisterDependency(conditions->getAddCursorForPre() - 1)->setExcludeGPR0();
   conditions->getPostConditions()->getRegisterDependency(conditions->getAddCursorForPost() - 1)->setExcludeGPR0();
   TR::addDependency(conditions, srcReg, TR::RealRegister::NoReg, TR_GPR, cg);
   conditions->getPreConditions()->getRegisterDependency(conditions->getAddCursorForPre() - 1)->setExcludeGPR0();
   conditions->getPostConditions()->getRegisterDependency(conditions->getAddCursorForPost() - 1)->setExcludeGPR0();
   TR::addDependency(conditions, temp1Reg, TR::RealRegister::NoReg, TR_GPR, cg);
   conditions->getPreConditions()->getRegisterDependency(conditions->getAddCursorForPre() - 1)->setExcludeGPR0();
   conditions->getPostConditions()->getRegisterDependency(conditions->getAddCursorForPost() - 1)->setExcludeGPR0();
   TR::addDependency(conditions, temp2Reg, TR::RealRegister::NoReg, TR_GPR, cg);
   conditions->getPreConditions()->getRegisterDependency(conditions->getAddCursorForPre() - 1)->setExcludeGPR0();
   conditions->getPostConditions()->getRegisterDependency(conditions->getAddCursorForPost() - 1)->setExcludeGPR0();
   TR::addDependency(conditions, temp3Reg, TR::RealRegister::NoReg, TR_GPR, cg);
   conditions->getPreConditions()->getRegisterDependency(conditions->getAddCursorForPre() - 1)->setExcludeGPR0();
   conditions->getPostConditions()->getRegisterDependency(conditions->getAddCursorForPost() - 1)->setExcludeGPR0();
   TR::addDependency(conditions, temp4Reg, TR::RealRegister::NoReg, TR_GPR, cg);
   conditions->getPreConditions()->getRegisterDependency(conditions->getAddCursorForPre() - 1)->setExcludeGPR0();
   conditions->getPostConditions()->getRegisterDependency(conditions->getAddCursorForPost() - 1)->setExcludeGPR0();
   TR::addDependency(conditions, condReg, TR::RealRegister::cr0, TR_CCR, cg);

   //--Generate Test to see if 'sourceRegister' is NULL
   generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpli, node, condReg, srcReg, NULLVALUE);
   //--Generate Jump past ArrayStoreCHKEvaluator
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, wbLabel, condReg);

   TR::LabelSymbol *helperCall = generateLabelSymbol(cg);
   VMarrayStoreCHKEvaluator(node, srcReg, dstReg, temp1Reg, temp2Reg, temp3Reg, temp4Reg, condReg, wbLabel, helperCall, cg);

   TR_PPCOutOfLineCodeSection *outlinedHelperCall = new (cg->trHeapMemory()) TR_PPCOutOfLineCodeSection(helperCallNode, TR::call, NULL, helperCall, wbLabel, cg);
   cg->getPPCOutOfLineCodeSectionList().push_front(outlinedHelperCall);
   cg->decReferenceCount(helperCallNode->getFirstChild());
   cg->decReferenceCount(helperCallNode->getSecondChild());

   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, wbLabel, conditions);
   cg->evaluate(firstChild);

   cg->decReferenceCount(firstChild);

   cg->stopUsingRegister(temp1Reg);
   cg->stopUsingRegister(temp2Reg);
   cg->stopUsingRegister(temp3Reg);
   cg->stopUsingRegister(temp4Reg);
   cg->stopUsingRegister(condReg);

   return NULL;
   }

TR::Register *J9::Power::TreeEvaluator::ArrayCHKEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   return TR::TreeEvaluator::VMarrayCheckEvaluator(node, cg);
   }

TR::Register *J9::Power::TreeEvaluator::conditionalHelperEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   // There are two points which have to be considered for this evaluator:
   //  1) This conditional tree is of a special form which is lowered just
   //     before instruction selection. This means the conditional is invisible
   //     to the control flow analysis. As such, we have to treat it just as
   //     part of a basic block;
   //  2) The two opCodes using this evaluator are intended as quick JVMPI
   //     probes. They should not impact the normal code (in terms of performance).
   //     As such, their call evaluation will be specially treated not to go
   //     through the normal linkage. OTI will preserve all the registers as
   //     needed.

   TR::RegisterDependencyConditions *conditions;
   TR::Register *cndReg, *jumpReg, *valReg;
   TR::Node *testNode = node->getFirstChild(), *callNode = node->getSecondChild();
   TR::Node *firstChild = testNode->getFirstChild(), *secondChild = testNode->getSecondChild();
   int32_t value, i, numArgs = callNode->getNumChildren();

   conditions = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(4, 4, cg->trMemory());

   TR_ASSERT(numArgs <= 2, "Unexpected number of arguments for helper.");

   // Helper arguments are in reversed order of the private linkage
   // Argument registers are not needed to be split since the helper will
   // preserve all of them.
   int32_t iArgIndex = 0, fArgIndex = 0;
   TR::Linkage *linkage = cg->createLinkage(TR_Private);
   for (i = numArgs - 1; i >= 0; i--)
      {
      TR::Register *argReg = cg->evaluate(callNode->getChild(i));
      TR::addDependency(conditions, argReg, (argReg->getKind() == TR_GPR) ? // Didn't consider Long here
            linkage->getProperties().getIntegerArgumentRegister(iArgIndex++) : linkage->getProperties().getFloatArgumentRegister(fArgIndex++), argReg->getKind(), cg);
      }

   cndReg = cg->allocateRegister(TR_CCR);
   jumpReg = cg->evaluate(firstChild);
   TR::addDependency(conditions, cndReg, TR::RealRegister::NoReg, TR_CCR, cg);
   TR::addDependency(conditions, jumpReg, TR::RealRegister::gr11, TR_GPR, cg);

   if (secondChild->getOpCode().isLoadConst() && (value = secondChild->getInt()) <= UPPER_IMMED && value >= LOWER_IMMED)
      {
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, testNode, cndReg, jumpReg, value);
      }
   else
      {
      valReg = cg->evaluate(secondChild);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp4, testNode, cndReg, jumpReg, valReg);
      }

   TR::Instruction *gcPoint;
   TR::LabelSymbol *snippetLabel = cg->lookUpSnippet(TR::Snippet::IsHelperCall, node->getSymbolReference());
   if (snippetLabel == NULL)
      {
      snippetLabel = generateLabelSymbol(cg);
      cg->addSnippet(new (cg->trHeapMemory()) TR::PPCHelperCallSnippet(cg, node, snippetLabel, node->getSymbolReference()));
      }
   gcPoint = generateDepConditionalBranchInstruction(cg, testNode->getOpCodeValue() == TR::icmpeq ? TR::InstOpCode::beql : TR::InstOpCode::bnel, node, snippetLabel, cndReg, conditions);
   gcPoint->PPCNeedsGCMap(0xFFFFFFFF);

   cg->stopUsingRegister(cndReg);
   for (i = numArgs - 1; i >= 0; i--)
      cg->decReferenceCount(callNode->getChild(i));
   cg->decReferenceCount(firstChild);
   cg->decReferenceCount(secondChild);
   cg->decReferenceCount(testNode);
   cg->decReferenceCount(callNode);
   return NULL;
   }

TR::Register *J9::Power::TreeEvaluator::flushEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR::ILOpCodes opCode = node->getOpCodeValue();

   if (opCode == TR::allocationFence)
      {
      if (!node->canOmitSync())
         {
         // If the following node will issue an lwsync then skip emitting one now. EscapeAnalysis can purposefully place
         // an AllocationFence before a monexit or volatile access so that the codegen can optimize the memory flush
         TR::TreeTop *tt = cg->getCurrentEvaluationTreeTop()->getNextTreeTop();
         TR::Node *node = tt->getNode();
         TR::Node *child = (node->getNumChildren() >= 1) ? node->getFirstChild() : NULL;
         if (!child || (node->getOpCodeValue() != TR::monexit && child->getOpCodeValue() != TR::monexit))
            {
            // Iterate the next tree to see if there is a resolved acquire/release load/store node that has yet to be evaluated
            // An unresolved acquire/release might not actually be an acquire/release access, and therefore can not replace the AllocationFence
            // A node that is already evaluated will not actually emit an 'lwsync' so it can not replace the AllocationFence
            // NOTE: As things currently stand, we won't ever encounter an unresolved symref that is strictly acquire/release,
            // but the following code will handle unresolved volatile symrefs as well
            bool fencedAccessFound = false;
            for (TR::PreorderNodeIterator it(tt, cg->comp()); it.currentTree() == tt; ++it)
               {
               node = it.currentNode();
               if (node->getOpCode().hasSymbolReference() &&
                   !node->hasUnresolvedSymbolReference() &&
                   node->getSymbolReference()->getSymbol()->isAtLeastOrStrongerThanAcquireRelease() &&
                   !node->getRegister())
                  {
                  fencedAccessFound = true;
                  break;
                  }
               }
            if (!fencedAccessFound)
               generateInstruction(cg, TR::InstOpCode::lwsync, node);
            }
         }
      }
   else
      {
      if (opCode == TR::loadFence)
         {
         if (cg->comp()->target().cpu.is(OMR_PROCESSOR_PPC_P7))
            generateInstruction(cg, TR::InstOpCode::lwsync, node);
         else
            generateInstruction(cg, TR::InstOpCode::isync, node);
         }
      else if (opCode == TR::storeFence)
         generateInstruction(cg, TR::InstOpCode::lwsync, node);
      else if (opCode == TR::fullFence)
         {
         if (node->canOmitSync())
            generateLabelInstruction(cg, TR::InstOpCode::label, node, generateLabelSymbol(cg));
         else
            generateInstruction(cg, TR::InstOpCode::sync, node);
         }
      }

   return NULL;
   }

static
void genCheckCastTransitionToNextSequence(TR::Node *node, J9::TreeEvaluator::InstanceOfOrCheckCastSequences *iter, TR::LabelSymbol *nextSequenceLabel, TR::LabelSymbol *trueLabel, TR::Register *condReg, TR::CodeGenerator *cg)
   {
   J9::TreeEvaluator::InstanceOfOrCheckCastSequences current = *iter++;

   if (current == J9::TreeEvaluator::EvaluateCastClass || current == J9::TreeEvaluator::LoadObjectClass)
      return;
   if (current == J9::TreeEvaluator::NullTest && node->getOpCodeValue() == TR::checkcastAndNULLCHK)
      return;

   J9::TreeEvaluator::InstanceOfOrCheckCastSequences next = *iter;

   while (next == J9::TreeEvaluator::EvaluateCastClass || next == J9::TreeEvaluator::LoadObjectClass)
      next = *++iter;

   bool nextSequenceIsOutOfLine = next == J9::TreeEvaluator::HelperCall || next == J9::TreeEvaluator::GoToFalse;

   if (nextSequenceIsOutOfLine)
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, nextSequenceLabel, condReg);
   else
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, trueLabel, condReg);
   }

static
void genInstanceOfTransitionToNextSequence(TR::Node *node, J9::TreeEvaluator::InstanceOfOrCheckCastSequences *iter, TR::LabelSymbol *nextSequenceLabel, TR::LabelSymbol *doneLabel, TR::Register *condReg, TR::Register *resultReg, bool resultRegInitialValue, TR::LabelSymbol *oppositeResultLabel, bool profiledClassIsInstanceOf, TR::CodeGenerator *cg)
   {
   J9::TreeEvaluator::InstanceOfOrCheckCastSequences current = *iter++;

   if (current == J9::TreeEvaluator::EvaluateCastClass || current == J9::TreeEvaluator::LoadObjectClass)
      return;
   if (current == J9::TreeEvaluator::NullTest && node->getOpCodeValue() == TR::checkcastAndNULLCHK)
      return;
   if (current == J9::TreeEvaluator::GoToFalse)
      return;

   J9::TreeEvaluator::InstanceOfOrCheckCastSequences next = *iter;

   while (next == J9::TreeEvaluator::EvaluateCastClass || next == J9::TreeEvaluator::LoadObjectClass)
      next = *++iter;

   if (next == J9::TreeEvaluator::GoToFalse && resultRegInitialValue == false)
      nextSequenceLabel = doneLabel;

   bool nextSequenceIsOutOfLine = next == J9::TreeEvaluator::HelperCall;

   if (current == J9::TreeEvaluator::NullTest)
      {
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, resultRegInitialValue == false ? doneLabel : oppositeResultLabel, condReg);
      }
   else if (current == J9::TreeEvaluator::CastClassCacheTest)
      {
      if (resultRegInitialValue == true)
         {
         if (nextSequenceIsOutOfLine)
            {
            generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, doneLabel, condReg);
            generateConditionalBranchInstruction(cg, TR::InstOpCode::bgt, node, nextSequenceLabel, condReg);
            // Fall through to oppositeResultLabel
            }
         else
            {
            generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, doneLabel, condReg);
            generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, oppositeResultLabel, condReg);
            // Fall through to nextSequenceLabel
            }
         }
      else
         {
         if (nextSequenceIsOutOfLine)
            {
            generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, condReg);
            generateConditionalBranchInstruction(cg, TR::InstOpCode::bgt, node, nextSequenceLabel, condReg);
            // Fall through to oppositeResultLabel
            }
         else
            {
            generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, condReg);
            generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, oppositeResultLabel, condReg);
            // Fall through to nextSequenceLabel
            }
         }
      }
   else if (current == J9::TreeEvaluator::ProfiledClassTest)
      {
      if (resultRegInitialValue == true)
         {
         if (profiledClassIsInstanceOf)
            {
            if (nextSequenceIsOutOfLine)
               {
               generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, condReg);
               generateLabelInstruction(cg, TR::InstOpCode::b, node, nextSequenceLabel);
               }
            else
               {
               generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, condReg);
               }
            }
         else
            {
            if (nextSequenceIsOutOfLine)
               {
               generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, nextSequenceLabel, condReg);
               // Fall through to oppositeResultLabel
               }
            else
               {
               generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, oppositeResultLabel, condReg);
               }
            }
         }
      else
         {
         if (profiledClassIsInstanceOf)
            {
            if (nextSequenceIsOutOfLine)
               {
               generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, nextSequenceLabel, condReg);
               // Fall through to oppositeResultLabel
               }
            else
               {
               generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, oppositeResultLabel, condReg);
               }
            }
         else
            {
            if (nextSequenceIsOutOfLine)
               {
               generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, condReg);
               generateLabelInstruction(cg, TR::InstOpCode::b, node, nextSequenceLabel);
               }
            else
               {
               generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, condReg);
               }
            }
         }
      }
   else
      {
      if (nextSequenceIsOutOfLine)
         generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, nextSequenceLabel, condReg);
      else
         generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, resultRegInitialValue == true ? doneLabel : oppositeResultLabel, condReg);
      }
   }

static
void genInstanceOfOrCheckCastClassEqualityTest(TR::Node *node, TR::Register *condReg, TR::Register *instanceClassReg, TR::Register *castClassReg, TR::CodeGenerator *cg)
   {
   generateTrg1Src2Instruction(cg, TR::InstOpCode::Op_cmpl, node, condReg, instanceClassReg, castClassReg);

   // At this point condReg[eq] will be set if the cast class matches the instance class. Caller is responsible for acting on the result.
   }

static
void genInstanceOfOrCheckCastSuperClassTest(TR::Node *node, TR::Register *condReg, TR::Register *instanceClassReg, TR::Register *castClassReg, int32_t castClassDepth, TR::LabelSymbol *falseLabel, TR_PPCScratchRegisterManager *srm, TR::CodeGenerator *cg)
   {
   TR::Compilation *comp = cg->comp();

   // When castClass is an unknown at compile time check if castClass is an interface or an array.
   // If so skip superClassTest.
   bool dynamicCastClass = castClassDepth == -1;
   if ( dynamicCastClass )
      {
      TR::Register *scratchRegister1 = srm->findOrCreateScratchRegister();
      TR::Register *scratchRegister2 = srm->findOrCreateScratchRegister();
      TR_ASSERT(node->getSecondChild()->getOpCodeValue() != TR::loadaddr,
            "genInstanceOfOrCheckCastSuperClassTest: castClassDepth == -1 is not supported for a loadaddr castClass");

      generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, scratchRegister1, TR::MemoryReference::createWithDisplacement(cg, castClassReg, offsetof(J9Class, romClass), TR::Compiler->om.sizeofReferenceAddress()));
      generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, scratchRegister1, TR::MemoryReference::createWithDisplacement(cg, scratchRegister1, offsetof(J9ROMClass, modifiers), 4));

      // Array and interface check
      loadConstant(cg, node, (J9AccClassArray | J9AccInterface), scratchRegister2);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::and_r, node, scratchRegister2, scratchRegister1, scratchRegister2);
      // At this point cr0[eq] will be set if this is not an interface or an array.
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, falseLabel, condReg);

      srm->reclaimScratchRegister(scratchRegister1);
      srm->reclaimScratchRegister(scratchRegister2);
      }

   // Compare the instance class depth to the cast class depth. If the instance class depth is less than or equal to
   // to the cast class depth then the cast class cannot be a superclass of the instance class.
   //
   TR::Register *instanceClassDepthReg = srm->findOrCreateScratchRegister();
   TR::Register *castClassDepthReg = NULL;
   generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, instanceClassDepthReg,
                              TR::MemoryReference::createWithDisplacement(cg, instanceClassReg, offsetof(J9Class, classDepthAndFlags), TR::Compiler->om.sizeofReferenceAddress()));
   static_assert(J9AccClassDepthMask < UINT_MAX, "Class depth is not containable in 32 bits");
   generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, instanceClassDepthReg, instanceClassDepthReg, 0, J9AccClassDepthMask);
   if (dynamicCastClass)
      {
      castClassDepthReg = srm->findOrCreateScratchRegister();
      generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, castClassDepthReg,
                                 TR::MemoryReference::createWithDisplacement(cg, castClassReg, offsetof(J9Class, classDepthAndFlags), TR::Compiler->om.sizeofReferenceAddress()));
      generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, castClassDepthReg, castClassDepthReg, 0, J9AccClassDepthMask);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp4, node, condReg, instanceClassDepthReg, castClassDepthReg);
      }
   else
      {
      if (castClassDepth <= UPPER_IMMED && castClassDepth >= LOWER_IMMED)
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, condReg, instanceClassDepthReg, castClassDepth);
      else
         {
         castClassDepthReg = srm->findOrCreateScratchRegister();
         loadConstant(cg, node, castClassDepth, castClassDepthReg);
         generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp4, node, condReg, instanceClassDepthReg, castClassDepthReg);
         }
      }

   srm->reclaimScratchRegister(instanceClassDepthReg);

   // At this point condReg[gt] will be set if the instance class depth is greater than the cast class depth.
   //
   generateConditionalBranchInstruction(cg, TR::InstOpCode::ble, node, falseLabel, condReg);

   // Load the superclasses array of the instance class and check if the superclass that appears at the depth of the cast class is in fact the cast class.
   // If not, the instance class and cast class are not in the same hierarchy.
   //
   TR::Register *instanceClassSuperClassesArrayReg = srm->findOrCreateScratchRegister();
   TR::Register *instanceClassSuperClassReg = srm->findOrCreateScratchRegister();

   generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, instanceClassSuperClassesArrayReg,
                              TR::MemoryReference::createWithDisplacement(cg, instanceClassReg, offsetof(J9Class, superclasses), TR::Compiler->om.sizeofReferenceAddress()));

   int32_t castClassDepthOffset = castClassDepth * TR::Compiler->om.sizeofReferenceAddress();
   if (dynamicCastClass)
      {
      if (comp->target().is64Bit())
         generateShiftLeftImmediateLong(cg, node, castClassDepthReg, castClassDepthReg, 3);
      else
         generateShiftLeftImmediate(cg, node, castClassDepthReg, castClassDepthReg, 2);
      generateTrg1MemInstruction(cg, TR::InstOpCode::Op_loadx, node, instanceClassSuperClassReg, TR::MemoryReference::createWithIndexReg(cg, instanceClassSuperClassesArrayReg, castClassDepthReg, TR::Compiler->om.sizeofReferenceAddress()));
      }
   else
      {
      if (castClassDepthOffset <= UPPER_IMMED && castClassDepthOffset >= LOWER_IMMED)
         {
         generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, instanceClassSuperClassReg,
                                    TR::MemoryReference::createWithDisplacement(cg, instanceClassSuperClassesArrayReg, castClassDepthOffset, TR::Compiler->om.sizeofReferenceAddress()));
         }
      else
         {
         if (!castClassDepthReg)
            {
            castClassDepthReg = srm->findOrCreateScratchRegister();
            if (0x00008000 == HI_VALUE(castClassDepthOffset))
               {
               generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addis, node, castClassDepthReg, instanceClassSuperClassesArrayReg, 0x7FFF);
               generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addis, node, castClassDepthReg, castClassDepthReg, 0x1);
               }
            else
               {
               generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addis, node, castClassDepthReg, instanceClassSuperClassesArrayReg, HI_VALUE(castClassDepthOffset));
               }
            generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, instanceClassSuperClassReg,
                                       TR::MemoryReference::createWithDisplacement(cg, castClassDepthReg, LO_VALUE(castClassDepthOffset), TR::Compiler->om.sizeofReferenceAddress()));
            }
         else
            {
            if (comp->target().is64Bit())
               generateShiftLeftImmediateLong(cg, node, castClassDepthReg, castClassDepthReg, 3);
            else
               generateShiftLeftImmediate(cg, node, castClassDepthReg, castClassDepthReg, 2);
            generateTrg1MemInstruction(cg, TR::InstOpCode::Op_loadx, node, instanceClassSuperClassReg, TR::MemoryReference::createWithIndexReg(cg, instanceClassSuperClassesArrayReg, castClassDepthReg, TR::Compiler->om.sizeofReferenceAddress()));
            }
         }
      }
   generateTrg1Src2Instruction(cg, TR::InstOpCode::Op_cmpl, node, condReg, instanceClassSuperClassReg, castClassReg);
   if (castClassDepthReg)
      srm->reclaimScratchRegister(castClassDepthReg);
   srm->reclaimScratchRegister(instanceClassSuperClassesArrayReg);
   srm->reclaimScratchRegister(instanceClassSuperClassReg);

   // At this point condReg[eq] will be set if the cast class is a superclass of the instance class. Caller is responsible for acting on the result.
   }

static
void genInstanceOfOrCheckCastArbitraryClassTest(TR::Node *node, TR::Register *condReg, TR::Register *instanceClassReg, TR_OpaqueClassBlock *arbitraryClass, TR_PPCScratchRegisterManager *srm, TR::CodeGenerator *cg)
   {
   TR::Compilation *comp = cg->comp();
   TR::Register *arbitraryClassReg = srm->findOrCreateScratchRegister();
   if (comp->compileRelocatableCode() && comp->getOption(TR_UseSymbolValidationManager))
      {
      TR::StaticSymbol *sym = TR::StaticSymbol::create(comp->trHeapMemory(), TR::Address);
      sym->setStaticAddress(arbitraryClass);
      sym->setClassObject();

      loadAddressConstant(cg, true, node, (intptr_t) new (comp->trHeapMemory()) TR::SymbolReference(comp->getSymRefTab(), sym), arbitraryClassReg, NULL, false, TR_ClassAddress);
      }
   else
      {
      loadAddressConstant(cg, comp->compileRelocatableCode(), node, (intptr_t)arbitraryClass, arbitraryClassReg);
      }
   genInstanceOfOrCheckCastClassEqualityTest(node, condReg, instanceClassReg, arbitraryClassReg, cg);
   srm->reclaimScratchRegister(arbitraryClassReg);

   // At this point condReg[eq] will be set if the cast class matches the arbitrary class. Caller is responsible for acting on the result.
   }

static
void genInstanceOfCastClassCacheTest(TR::Node *node, TR::Register *condReg, TR::Register *instanceClassReg, TR::Register *castClassReg, TR_PPCScratchRegisterManager *srm, TR::CodeGenerator *cg)
   {
   TR::Compilation *comp = cg->comp();

   // Compare the cast class against the cache on the instance class.
   // If they are the same the cast is successful.
   // If not it's either because the cache class does not match the cast class, or it does match except the cache class has the low bit set, which means the cast is not successful.
   //
   TR::Register *castClassCacheReg = srm->findOrCreateScratchRegister();
   generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, castClassCacheReg,
                              TR::MemoryReference::createWithDisplacement(cg, instanceClassReg, offsetof(J9Class, castClassCache), TR::Compiler->om.sizeofReferenceAddress()));
   generateTrg1Src2Instruction(cg, TR::InstOpCode::XOR, node, castClassCacheReg, castClassCacheReg, castClassReg);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::Op_cmpli, node, condReg, castClassCacheReg, 1);

   // At this point condReg[lt] will be set if the cast is successful, condReg[eq] will be set if the cast is unsuccessful, and condReg[gt] will be set if the cache class did not match the cast class.
   // Caller is responsible for acting on the result.
   }

static
void genCheckCastCastClassCacheTest(TR::Node *node, TR::Register *condReg, TR::Register *instanceClassReg, TR::Register *castClassReg, TR_PPCScratchRegisterManager *srm, TR::CodeGenerator *cg)
   {
   TR::Compilation *comp = cg->comp();

   // Compare the cast class against the cache on the instance class.
   // If they are the same the cast is successful.
   // If not it's either because the cache class does not match the cast class, or it does match except the cache class has the low bit set, which means the cast is invalid.
   //
   TR::Register *castClassCacheReg = srm->findOrCreateScratchRegister();
   generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, castClassCacheReg,
                              TR::MemoryReference::createWithDisplacement(cg, instanceClassReg, offsetof(J9Class, castClassCache), TR::Compiler->om.sizeofReferenceAddress()));
   generateTrg1Src2Instruction(cg, TR::InstOpCode::Op_cmpl, node, condReg, castClassReg, castClassCacheReg);
   srm->reclaimScratchRegister(castClassCacheReg);

   // At this point condReg[eq] will be set if the cast is successful. Caller is responsible for acting on the result.
   }

static
void genInstanceOfOrCheckCastObjectArrayTest(TR::Node *node, TR::Register *cr0Reg, TR::Register *instanceClassReg, TR::LabelSymbol *falseLabel, TR_PPCScratchRegisterManager *srm, TR::CodeGenerator *cg)
   {
   TR::Compilation *comp = cg->comp();

   // Load the object ROM class and test the modifiers to see if this is an array.
   //
   TR::Register *scratchReg = srm->findOrCreateScratchRegister();
   generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, scratchReg, TR::MemoryReference::createWithDisplacement(cg, instanceClassReg, offsetof(J9Class, romClass), TR::Compiler->om.sizeofReferenceAddress()));
   generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, scratchReg, TR::MemoryReference::createWithDisplacement(cg, scratchReg, offsetof(J9ROMClass, modifiers), 4));
   if (J9AccClassArray <= UPPER_IMMED && J9AccClassArray >= LOWER_IMMED)
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, scratchReg, scratchReg, cr0Reg, J9AccClassArray);
   else
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andis_r, node, scratchReg, scratchReg, cr0Reg, J9AccClassArray >> 16);

   // At this point cr0[eq] will be set if this is not an array.
   //
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, falseLabel, cr0Reg);

   // If it's an array, load the component ROM class and test the modifiers to see if this is a primitive array.
   //
   generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, scratchReg, TR::MemoryReference::createWithDisplacement(cg, instanceClassReg, offsetof(J9ArrayClass, componentType), TR::Compiler->om.sizeofReferenceAddress()));
   generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, scratchReg, TR::MemoryReference::createWithDisplacement(cg, scratchReg, offsetof(J9Class, romClass), TR::Compiler->om.sizeofReferenceAddress()));
   generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, scratchReg, TR::MemoryReference::createWithDisplacement(cg, scratchReg, offsetof(J9ROMClass, modifiers), 4));
   if (J9AccClassInternalPrimitiveType <= UPPER_IMMED && J9AccClassInternalPrimitiveType >= LOWER_IMMED)
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, scratchReg, scratchReg, cr0Reg, J9AccClassInternalPrimitiveType);
   else
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andis_r, node, scratchReg, scratchReg, cr0Reg, J9AccClassInternalPrimitiveType >> 16);
   srm->reclaimScratchRegister(scratchReg);

   // At this point cr0[eq] will be set if this is not a primitive array. Caller is responsible acting on the result.
   }

static
void genInstanceOfOrCheckCastHelperCall(TR::Node *node, TR::Register *objectReg, TR::Register *castClassReg, TR::Register *resultReg, TR::CodeGenerator *cg)
   {
   TR_ASSERT((resultReg != NULL) == (node->getOpCodeValue() == TR::instanceof),
             "Expecting result reg for instanceof but not checkcast");

   TR::Register *helperReturnReg = resultReg ? cg->allocateRegister() : NULL;

   TR::RegisterDependencyConditions *deps = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(1, 4, cg->trMemory());

   deps->addPreCondition(castClassReg, TR::RealRegister::gr3);
   deps->addPostCondition(resultReg ? helperReturnReg : castClassReg, TR::RealRegister::gr3);
   deps->addPostCondition(objectReg, TR::RealRegister::gr4);
   TR::Register *dummyReg, *dummyRegCCR;
   // gr11 is killed by the trampoline.
   deps->addPostCondition(dummyReg = cg->allocateRegister(), TR::RealRegister::gr11);
   dummyReg->setPlaceholderReg();
   // cr0 is not preserved by the helper.
   deps->addPostCondition(dummyRegCCR = cg->allocateRegister(TR_CCR), TR::RealRegister::cr0);
   dummyRegCCR->setPlaceholderReg();

   TR::Instruction *gcPoint = generateDepImmSymInstruction(cg, TR::InstOpCode::bl, node, (uintptr_t)node->getSymbolReference()->getMethodAddress(), deps, node->getSymbolReference());
   gcPoint->PPCNeedsGCMap(0xFFFFFFFF);

   if (resultReg)
      generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, resultReg, helperReturnReg);

   cg->stopUsingRegister(dummyReg);
   cg->stopUsingRegister(dummyRegCCR);

   cg->machine()->setLinkRegisterKilled(true);

   TR::Register *nodeRegs[3] = {objectReg, castClassReg, resultReg};
   deps->stopUsingDepRegs(cg, 3, nodeRegs);
   }

TR::Register *J9::Power::TreeEvaluator::VMcheckcastEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR::Compilation                      *comp = cg->comp();
   TR_J9VMBase                          *fej9 = reinterpret_cast<TR_J9VMBase *>(comp->fe());
   TR_OpaqueClassBlock                  *compileTimeGuessClass;
   InstanceOfOrCheckCastProfiledClasses  profiledClassesList[1];
   InstanceOfOrCheckCastSequences        sequences[InstanceOfOrCheckCastMaxSequences];
   uint32_t                              numberOfProfiledClass = 0;
   float                                 topClassProbability = 0.0;
   bool                                  topClassWasCastClass = false;
   uint32_t                              numSequencesRemaining = calculateInstanceOfOrCheckCastSequences(node, sequences, &compileTimeGuessClass, cg, profiledClassesList, &numberOfProfiledClass, 1, &topClassProbability, &topClassWasCastClass);

   TR::Node        *objectNode = node->getFirstChild();
   TR::Node        *castClassNode = node->getSecondChild();
   TR::Register    *objectReg = cg->evaluate(objectNode);
   TR::Register    *castClassReg = NULL;

   TR::LabelSymbol *depLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *doneLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *helperReturnLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *nextSequenceLabel = generateLabelSymbol(cg);

   TR::Instruction *gcPoint;

   TR_PPCScratchRegisterManager *srm = cg->generateScratchRegisterManager();
   TR::Register                 *objectClassReg = NULL;
   TR::Register                 *cr0Reg = cg->allocateRegister(TR_CCR);

   InstanceOfOrCheckCastSequences *iter = &sequences[0];
   while (numSequencesRemaining > 1)
      {
      switch (*iter)
         {
         case EvaluateCastClass:
            TR_ASSERT(!castClassReg, "Cast class already evaluated");
            castClassReg = cg->evaluate(castClassNode);
            break;
         case LoadObjectClass:
            TR_ASSERT(!objectClassReg, "Object class already loaded");
            objectClassReg = srm->findOrCreateScratchRegister();
            generateLoadJ9Class(node, objectClassReg, objectReg, cg);
            break;
         case NullTest:
            if (comp->getOption(TR_TraceCG)) traceMsg(comp, "%s: Emitting NullTest\n", node->getOpCode().getName());
            TR_ASSERT(!objectNode->isNonNull(), "Object is known to be non-null, no need for a null test");
            if (node->getOpCodeValue() == TR::checkcastAndNULLCHK)
               {
               TR::Node *nullChkInfo = comp->findNullChkInfo(node);
               gcPoint = generateNullTestInstructions(cg, objectReg, nullChkInfo, !cg->getHasResumableTrapHandler());
               gcPoint->PPCNeedsGCMap(0x0);
               }
            else
               {
               genNullTest(node, objectReg, cr0Reg, cg);
               }
            break;
         case GoToTrue:
            TR_ASSERT(false, "Doesn't make sense, GoToTrue should not be part of multiple sequences");
            break;
         case GoToFalse:
            TR_ASSERT(false, "Doesn't make sense, GoToFalse should be the terminal sequence");
            break;
         case ClassEqualityTest:
            if (comp->getOption(TR_TraceCG)) traceMsg(comp, "%s: Emitting ClassEqualityTest\n", node->getOpCode().getName());
            genInstanceOfOrCheckCastClassEqualityTest(node, cr0Reg, objectClassReg, castClassReg, cg);
            break;
         case SuperClassTest:
            {
            if (comp->getOption(TR_TraceCG)) traceMsg(comp, "%s: Emitting SuperClassTest\n", node->getOpCode().getName());
            int32_t castClassDepth = castClassNode->getSymbolReference()->classDepth(comp);
            genInstanceOfOrCheckCastSuperClassTest(node, cr0Reg, objectClassReg, castClassReg, castClassDepth, nextSequenceLabel, srm, cg);
            break;
            }
         case ProfiledClassTest:
            if (comp->getOption(TR_TraceCG)) traceMsg(comp, "%s: Emitting ProfiledClassTest\n", node->getOpCode().getName());
            genInstanceOfOrCheckCastArbitraryClassTest(node, cr0Reg, objectClassReg, profiledClassesList[0].profiledClass, srm, cg);
            break;
         case CompileTimeGuessClassTest:
            if (comp->getOption(TR_TraceCG)) traceMsg(comp, "%s: Emitting CompileTimeGuessClassTest\n", node->getOpCode().getName());
            genInstanceOfOrCheckCastArbitraryClassTest(node, cr0Reg, objectClassReg, compileTimeGuessClass, srm, cg);
            break;
         case CastClassCacheTest:
            if (comp->getOption(TR_TraceCG)) traceMsg(comp, "%s: Emitting CastClassCacheTest\n", node->getOpCode().getName());
            genCheckCastCastClassCacheTest(node, cr0Reg, objectClassReg, castClassReg, srm, cg);
            break;
         case ArrayOfJavaLangObjectTest:
            if (comp->getOption(TR_TraceCG)) traceMsg(comp, "%s: Emitting ArrayOfJavaLangObjectTest\n", node->getOpCode().getName());
            genInstanceOfOrCheckCastObjectArrayTest(node, cr0Reg, objectClassReg, nextSequenceLabel, srm, cg);
            break;
         case HelperCall:
            TR_ASSERT(false, "Doesn't make sense, HelperCall should be the terminal sequence");
            break;
         default:
            break;
         }

      genCheckCastTransitionToNextSequence(node, iter, nextSequenceLabel, doneLabel, cr0Reg, cg);

      --numSequencesRemaining;
      ++iter;

      if (*iter != HelperCall && *iter != GoToFalse)
         {
         generateLabelInstruction(cg, TR::InstOpCode::label, node, nextSequenceLabel);
         nextSequenceLabel = generateLabelSymbol(cg);
         }
      }

   if (objectClassReg)
      srm->reclaimScratchRegister(objectClassReg);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel);

   cg->generateDebugCounter(TR::DebugCounter::debugCounterName(comp, "instanceOfOrCheckCast/%s/fastPath",
                                                   node->getOpCode().getName()),
                            *srm);
   cg->generateDebugCounter(TR::DebugCounter::debugCounterName(comp, "instanceOfOrCheckCast.perMethod/%s/(%s)/%d/%d/fastPath",
                                                   node->getOpCode().getName(), comp->signature(), node->getByteCodeInfo().getCallerIndex(), node->getByteCodeInfo().getByteCodeIndex()),
                            *srm);

   TR::RegisterDependencyConditions *deps = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, 3 + srm->numAvailableRegisters(), cg->trMemory());
   srm->addScratchRegistersToDependencyList(deps, true);
   deps->addPostCondition(cr0Reg, TR::RealRegister::cr0);
   deps->addPostCondition(objectReg, TR::RealRegister::NoReg);
   deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();
   if (castClassReg)
      {
      deps->addPostCondition(castClassReg, TR::RealRegister::NoReg);
      deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();
      }

   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, depLabel, deps);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, helperReturnLabel);

   if (numSequencesRemaining > 0 && *iter != GoToTrue)
      {
      TR_ASSERT(*iter == HelperCall || *iter == GoToFalse, "Expecting helper call or fail here");
      bool helperCallForFailure = *iter != HelperCall;
      if (comp->getOption(TR_TraceCG)) traceMsg(comp, "%s: Emitting HelperCall%s\n", node->getOpCode().getName(), helperCallForFailure ? " for failure" : "");
      TR_PPCOutOfLineCodeSection *outlinedHelperCall = new (cg->trHeapMemory()) TR_PPCOutOfLineCodeSection(node, TR::call, NULL, nextSequenceLabel, helperReturnLabel, cg);
      cg->generateDebugCounter(nextSequenceLabel->getInstruction(),
                               TR::DebugCounter::debugCounterName(comp, "instanceOfOrCheckCast/%s/%s",
                                                                  node->getOpCode().getName(),
                                                                  helperCallForFailure ? "fail" : "helperCall"));
      cg->generateDebugCounter(nextSequenceLabel->getInstruction(),
                               TR::DebugCounter::debugCounterName(comp, "instanceOfOrCheckCast.perMethod/%s/(%s)/%d/%d/%s",
                                                                  node->getOpCode().getName(), comp->signature(), node->getByteCodeInfo().getCallerIndex(), node->getByteCodeInfo().getByteCodeIndex(),
                                                                  helperCallForFailure ? "fail" : "helperCall"));

      cg->getPPCOutOfLineCodeSectionList().push_front(outlinedHelperCall);
      }

   deps->stopUsingDepRegs(cg, objectReg, castClassReg);
   cg->decReferenceCount(objectNode);
   cg->decReferenceCount(castClassNode);

   return NULL;
   }

TR::Register *J9::Power::TreeEvaluator::VMinstanceOfEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR::Compilation                      *comp = cg->comp();
   TR_OpaqueClassBlock                  *compileTimeGuessClass;
   bool                                  profiledClassIsInstanceOf;
   InstanceOfOrCheckCastProfiledClasses  profiledClassesList[1];
   InstanceOfOrCheckCastSequences        sequences[InstanceOfOrCheckCastMaxSequences];
   bool                                  topClassWasCastClass = false;
   float                                 topClassProbability = 0.0;
   uint32_t                              numberOfProfiledClass;
   uint32_t                              numSequencesRemaining = calculateInstanceOfOrCheckCastSequences(node, sequences, &compileTimeGuessClass, cg, profiledClassesList, &numberOfProfiledClass, 1, &topClassProbability, &topClassWasCastClass);

   TR::Node                       *objectNode = node->getFirstChild();
   TR::Node                       *castClassNode = node->getSecondChild();
   TR::Register                   *objectReg = cg->evaluate(objectNode);
   TR::Register                   *castClassReg = NULL;
   TR::Register                   *resultReg = cg->allocateRegister();

   TR::LabelSymbol *depLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *doneLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *helperReturnLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *nextSequenceLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *oppositeResultLabel = generateLabelSymbol(cg);

   TR::Instruction *gcPoint;

   TR_PPCScratchRegisterManager *srm = cg->generateScratchRegisterManager();
   TR::Register                 *objectClassReg = NULL;
   TR::Register                 *cr0Reg = cg->allocateRegister(TR_CCR);

   static bool initialResult = feGetEnv("TR_instanceOfInitialValue") != NULL;//true;
   generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, initialResult);

   InstanceOfOrCheckCastSequences *iter = &sequences[0];
   while (numSequencesRemaining > 1 || (numSequencesRemaining == 1 && *iter != HelperCall))
      {
      switch (*iter)
         {
         case EvaluateCastClass:
            TR_ASSERT(!castClassReg, "Cast class already evaluated");
            castClassReg = cg->evaluate(castClassNode);
            break;
         case LoadObjectClass:
            TR_ASSERT(!objectClassReg, "Object class already loaded");
            objectClassReg = srm->findOrCreateScratchRegister();
            generateLoadJ9Class(node, objectClassReg, objectReg, cg);
            break;
         case NullTest:
            if (comp->getOption(TR_TraceCG)) traceMsg(comp, "%s: Emitting NullTest\n", node->getOpCode().getName());
            TR_ASSERT(!objectNode->isNonNull(), "Object is known to be non-null, no need for a null test");
            if (node->getOpCodeValue() == TR::checkcastAndNULLCHK)
               {
               TR::Node *nullChkInfo = comp->findNullChkInfo(node);
               gcPoint = generateNullTestInstructions(cg, objectReg, nullChkInfo, !cg->getHasResumableTrapHandler());
               gcPoint->PPCNeedsGCMap(0x0);
               }
            else
               {
               genNullTest(node, objectReg, cr0Reg, cg);
               }
            break;
         case GoToTrue:
            if (comp->getOption(TR_TraceCG)) traceMsg(comp, "%s: Emitting GoToTrue\n", node->getOpCode().getName());
            if (initialResult == true)
               generateLabelInstruction(cg, TR::InstOpCode::b, node, doneLabel);
            break;
         case GoToFalse:
            if (comp->getOption(TR_TraceCG)) traceMsg(comp, "%s: Emitting GoToFalse\n", node->getOpCode().getName());
            if (initialResult == false)
               generateLabelInstruction(cg, TR::InstOpCode::b, node, doneLabel);
            break;
         case ClassEqualityTest:
            if (comp->getOption(TR_TraceCG)) traceMsg(comp, "%s: Emitting ClassEqualityTest\n", node->getOpCode().getName());
            genInstanceOfOrCheckCastClassEqualityTest(node, cr0Reg, objectClassReg, castClassReg, cg);
            break;
         case SuperClassTest:
            {
            if (comp->getOption(TR_TraceCG)) traceMsg(comp, "%s: Emitting SuperClassTest\n", node->getOpCode().getName());
            int32_t castClassDepth = castClassNode->getSymbolReference()->classDepth(comp);
            genInstanceOfOrCheckCastSuperClassTest(node, cr0Reg, objectClassReg, castClassReg, castClassDepth, nextSequenceLabel, srm, cg);
            break;
            }
         case ProfiledClassTest:
            if (comp->getOption(TR_TraceCG)) traceMsg(comp, "%s: Emitting ProfiledClassTest\n", node->getOpCode().getName());
            profiledClassIsInstanceOf = profiledClassesList[0].isProfiledClassInstanceOfCastClass;
            genInstanceOfOrCheckCastArbitraryClassTest(node, cr0Reg, objectClassReg, profiledClassesList[0].profiledClass, srm, cg);
            break;
         case CompileTimeGuessClassTest:
            if (comp->getOption(TR_TraceCG)) traceMsg(comp, "%s: Emitting CompileTimeGuessClassTest\n", node->getOpCode().getName());
            genInstanceOfOrCheckCastArbitraryClassTest(node, cr0Reg, objectClassReg, compileTimeGuessClass, srm, cg);
            break;
         case CastClassCacheTest:
            if (comp->getOption(TR_TraceCG)) traceMsg(comp, "%s: Emitting CastClassCacheTest\n", node->getOpCode().getName());
            genInstanceOfCastClassCacheTest(node, cr0Reg, objectClassReg, castClassReg, srm, cg);
            break;
         case ArrayOfJavaLangObjectTest:
            if (comp->getOption(TR_TraceCG)) traceMsg(comp, "%s: Emitting ArrayOfJavaLangObjectTest\n", node->getOpCode().getName());
            genInstanceOfOrCheckCastObjectArrayTest(node, cr0Reg, objectClassReg, nextSequenceLabel, srm, cg);
            break;
         case DynamicCacheObjectClassTest:
            TR_ASSERT_FATAL(false, "%s: DynamicCacheObjectClassTest is not implemented on P\n", node->getOpCode().getName());
            break;
         case DynamicCacheDynamicCastClassTest:
            TR_ASSERT_FATAL(false, "%s: DynamicCacheDynamicCastClassTest is not implemented on P\n", node->getOpCode().getName());
            break;
         case HelperCall:
            TR_ASSERT(false, "Doesn't make sense, HelperCall should be the terminal sequence");
            break;
         default:
            break;
         }

      if (--numSequencesRemaining > 0)
         {
         genInstanceOfTransitionToNextSequence(node, iter, nextSequenceLabel, doneLabel, cr0Reg, resultReg, initialResult, oppositeResultLabel, profiledClassIsInstanceOf, cg);
         if (*++iter != HelperCall)
            {
            generateLabelInstruction(cg, TR::InstOpCode::label, node, nextSequenceLabel);
            nextSequenceLabel = generateLabelSymbol(cg);
            }
         }
      }

   if (objectClassReg)
      srm->reclaimScratchRegister(objectClassReg);

   if (true)
      {
      generateLabelInstruction(cg, TR::InstOpCode::label, node, oppositeResultLabel);
      generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, !initialResult);
      }

   generateLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel);

   cg->generateDebugCounter(TR::DebugCounter::debugCounterName(comp, "instanceOfOrCheckCast/%s/fastPath",
                                                               node->getOpCode().getName()),
                            *srm);
   cg->generateDebugCounter(TR::DebugCounter::debugCounterName(comp, "instanceOfOrCheckCast.perMethod/%s/(%s)/%d/%d/fastPath",
                                                               node->getOpCode().getName(), comp->signature(), node->getByteCodeInfo().getCallerIndex(), node->getByteCodeInfo().getByteCodeIndex()),
                            *srm);

   TR::RegisterDependencyConditions *deps = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, 4 + srm->numAvailableRegisters(), cg->trMemory());
   srm->addScratchRegistersToDependencyList(deps, true);
   deps->addPostCondition(cr0Reg, TR::RealRegister::cr0);
   deps->addPostCondition(resultReg, TR::RealRegister::NoReg);
   deps->addPostCondition(objectReg, TR::RealRegister::NoReg);
   deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();
   if (castClassReg)
      {
      deps->addPostCondition(castClassReg, TR::RealRegister::NoReg);
      deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();
      }

   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, depLabel, deps);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, helperReturnLabel);

   if (numSequencesRemaining > 0)
      {
      if (*iter == HelperCall)
         {
         if (comp->getOption(TR_TraceCG)) traceMsg(comp, "%s: Emitting HelperCall\n", node->getOpCode().getName());
         TR_PPCOutOfLineCodeSection *outlinedHelperCall = new (cg->trHeapMemory()) TR_PPCOutOfLineCodeSection(node, TR::icall, resultReg, nextSequenceLabel, helperReturnLabel, cg);
         cg->generateDebugCounter(nextSequenceLabel->getInstruction(),
                                  TR::DebugCounter::debugCounterName(comp, "instanceOfOrCheckCast/%s/helperCall",
                                                                     node->getOpCode().getName()));
         cg->generateDebugCounter(nextSequenceLabel->getInstruction(),
                                  TR::DebugCounter::debugCounterName(comp, "instanceOfOrCheckCast.perMethod/%s/(%s)/%d/%d/helperCall",
                                                                     node->getOpCode().getName(), comp->signature(), node->getByteCodeInfo().getCallerIndex(), node->getByteCodeInfo().getByteCodeIndex()));

         cg->getPPCOutOfLineCodeSectionList().push_front(outlinedHelperCall);
         }
      }

   // Stop using every reg in the deps except these ones.
   //
   TR::Register *nodeRegs[3] = {objectReg, castClassReg, resultReg};
   deps->stopUsingDepRegs(cg, 3, nodeRegs);

   cg->decReferenceCount(objectNode);
   cg->decReferenceCount(castClassNode);

   node->setRegister(resultReg);

   return resultReg;
   }

// ----------------------------------------------------------------------------

// only need a helper call if the class is not super and not final, otherwise
// it can be determined without a call-out
static bool needHelperCall(bool testCastClassIsSuper, bool isFinalClass)
   {
   return !testCastClassIsSuper && !isFinalClass;
   }

static bool needTestCache(bool cachingEnabled, bool needsHelperCall, bool superClassTest, TR_OpaqueClassBlock* castClassAddr, uint8_t num_PICS)
   {
   return cachingEnabled && needsHelperCall && !superClassTest && castClassAddr && num_PICS;
   }

TR::Register *J9::Power::TreeEvaluator::ifInstanceOfEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   return TR::TreeEvaluator::VMifInstanceOfEvaluator(node, cg);
   }


TR::Register *J9::Power::TreeEvaluator::VMifInstanceOfEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR::Node *depNode;
   int32_t depIndex;
   int32_t numDep;
   TR::LabelSymbol *branchLabel;
   TR::Node *instanceOfNode;
   TR::Node *valueNode;
   int32_t value;
   TR::LabelSymbol *doneLabel, *res0Label, *res1Label;
   bool branchOn1;
   TR::Register *resultReg;
   TR::ILOpCodes opCode;
   TR::Compilation * comp = cg->comp();
   TR::SymbolReference *castClassSymRef;
   TR::Node *castClassNode;
   TR_OpaqueClassBlock *castClassAddr;
   TR_OpaqueClassBlock *guessClassArray[NUM_PICS];

   depIndex = 0;
   numDep = 0;
   depNode = NULL;
   branchLabel = node->getBranchDestination()->getNode()->getLabel();
   instanceOfNode = node->getFirstChild();
   valueNode = node->getSecondChild();
   value = valueNode->getInt();
   opCode = node->getOpCodeValue();

   castClassNode = instanceOfNode->getSecondChild();
   castClassSymRef = castClassNode->getSymbolReference();
   castClassAddr = TR::TreeEvaluator::getCastClassAddress(castClassNode);
   uint8_t num_PICS = TR::TreeEvaluator::interpreterProfilingInstanceOfOrCheckCastInfo(cg, instanceOfNode, guessClassArray);

   bool testEqualClass = instanceOfOrCheckCastNeedEqualityTest(instanceOfNode, cg);
   // Testing castClassAddr to set testCastClassIsSuper only if cast class is known at compile time
   // When class is unknown, testCastClassIsSuper is false, and needsHelperCall is true to handle non-typical class types (e.g. interfaces)
   bool testCastClassIsSuper = castClassAddr && instanceOfOrCheckCastNeedSuperTest(instanceOfNode, cg);
   bool isFinalClass = (castClassSymRef == NULL) ? false : castClassSymRef->isNonArrayFinal(comp);
   bool needsHelperCall = needHelperCall(testCastClassIsSuper, isFinalClass);
   bool testCache = needTestCache(!comp->getOption(TR_DisableInlineCheckCast), needsHelperCall, testCastClassIsSuper, castClassAddr, num_PICS);

   if (node->getNumChildren() == 3)
      {
      depNode = node->getChild(2);
      numDep = depNode->getNumChildren();
      }

   // If the result itself is assigned to a global register, we still have to evaluate it
   int32_t needResult = (instanceOfNode->getReferenceCount() > 1);

   if (depNode != NULL)
      {
      int32_t i1;

      if (!needsHelperCall && numberOfRegisterCandidate(cg, depNode, TR_GPR) + 7 > cg->getMaximumNumberOfGPRsAllowedAcrossEdge(node))
         return ((TR::Register *) 1);

      const TR::PPCLinkageProperties& properties = cg->getLinkage()->getProperties();
      TR::Node *objNode = instanceOfNode->getFirstChild();
      for (i1 = 0; i1 < depNode->getNumChildren(); i1++)
         {
         TR::Node *childNode = depNode->getChild(i1);
         int32_t regIndex = cg->getGlobalRegister(childNode->getGlobalRegisterNumber());

         int32_t validHighRegNum = TR::TreeEvaluator::getHighGlobalRegisterNumberIfAny(childNode, cg);

         if (needsHelperCall)
            {
            int32_t highIndex;

            if (validHighRegNum != -1)
               highIndex = cg->getGlobalRegister(validHighRegNum);

            if (!properties.getPreserved((TR::RealRegister::RegNum) regIndex)
                  || (validHighRegNum != -1 && !properties.getPreserved((TR::RealRegister::RegNum) highIndex)))
               return ((TR::Register *) 1);
            }
         else
            {
            if (childNode->getOpCodeValue() == TR::PassThrough)
               childNode = childNode->getFirstChild();
            if ((childNode == objNode || childNode == castClassNode) && regIndex == TR::RealRegister::gr0)
               return ((TR::Register *) 1);
            }
         }
      depIndex = numberOfRegisterCandidate(cg, depNode, TR_GPR) + numberOfRegisterCandidate(cg, depNode, TR_FPR) +
                 numberOfRegisterCandidate(cg, depNode, TR_CCR) + numberOfRegisterCandidate(cg, depNode, TR_VRF) +
                 numberOfRegisterCandidate(cg, depNode, TR_VSX_SCALAR) + numberOfRegisterCandidate(cg, depNode, TR_VSX_VECTOR);
      }

   doneLabel = generateLabelSymbol(cg);

   if ((opCode == TR::ificmpeq && value == 1) || (opCode != TR::ificmpeq && value == 0))
      {
      res0Label = doneLabel;
      res1Label = branchLabel;
      branchOn1 = true;
      }
   else
      {
      res0Label = branchLabel;
      res1Label = doneLabel;
      branchOn1 = false;
      }

   resultReg = TR::TreeEvaluator::VMgenCoreInstanceofEvaluator(instanceOfNode, cg, true, depIndex, numDep, depNode, needResult, needsHelperCall, testEqualClass, testCache, testCastClassIsSuper,
         doneLabel, res0Label, res1Label, branchOn1);

   if (resultReg != instanceOfNode->getRegister())
      instanceOfNode->setRegister(resultReg);
   cg->decReferenceCount(instanceOfNode);
   cg->decReferenceCount(valueNode);

   node->setRegister(NULL);
   return NULL;
   }

// genCoreInstanceofEvaluator is used by if instanceof and instanceof routines.
// The routine generates the 'core' code for instanceof evaluation. It requires a true and false label
// (which are the same and are just fall-through labels if no branching is required) as well as
// a boolean to indicate if the result should be calculated and returned in a register.
// The code also needs to indicate if the fall-through case if for 'true' or 'false'.
TR::Register * J9::Power::TreeEvaluator::VMgenCoreInstanceofEvaluator(TR::Node * node, TR::CodeGenerator * cg, bool isVMifInstanceOf, int32_t depIndex, int32_t numDep,
      TR::Node *depNode, bool needResult, bool needHelperCall, bool testEqualClass, bool testCache, bool testCastClassIsSuper, TR::LabelSymbol *doneLabel,
      TR::LabelSymbol *res0Label, TR::LabelSymbol *res1Label, bool branchOn1)
   {
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());
   TR::Register *objectReg, *castClassReg, *objClassReg;
   TR::Register *resultReg, *crReg, *scratch1Reg, *scratch2Reg, *scratch3Reg;
   TR::Instruction *iCursor;
   TR::SymbolReference *castClassSymRef;
   TR::RegisterDependencyConditions *conditions;
   TR::LabelSymbol *trueLabel, *callLabel, *nextTestLabel;
   TR::Node *objectNode, *castClassNode;
   int32_t castClassDepth;
   TR::ILOpCodes opCode;
   TR::RegisterDependencyConditions *deps;
   TR_OpaqueClassBlock *castClassAddr;
   TR_OpaqueClassBlock *guessClassArray[NUM_PICS];
   TR::Compilation * comp = cg->comp();
   castClassDepth = -1;
   trueLabel = NULL;
   resultReg = NULL;
   deps = NULL;
   iCursor = NULL;

   castClassNode = node->getSecondChild();
   castClassSymRef = castClassNode->getSymbolReference();
   castClassAddr = TR::TreeEvaluator::getCastClassAddress(castClassNode);
   uint8_t num_PICS = TR::TreeEvaluator::interpreterProfilingInstanceOfOrCheckCastInfo(cg, node, guessClassArray);
   objectNode = node->getFirstChild();

   bool stopUsingHelperCallCR = false;

   if (needHelperCall)
      {
      callLabel = generateLabelSymbol(cg);

      TR::ILOpCodes opCode = node->getOpCodeValue();

      TR::Node::recreate(node, TR::icall);
      directCallEvaluator(node, cg);
      TR::Node::recreate(node, opCode);

      iCursor = cg->getAppendInstruction();
      while (iCursor->getOpCodeValue() != TR::InstOpCode::bl)
         iCursor = iCursor->getPrev();
      conditions = ((TR::PPCDepImmSymInstruction *) iCursor)->getDependencyConditions();
      iCursor = iCursor->getPrev();
      iCursor = generateLabelInstruction(cg, TR::InstOpCode::label, node, callLabel, iCursor);
      iCursor = iCursor->getPrev();

      auto firstArgReg = node->getSymbol()->castToMethodSymbol()->getLinkageConvention() == TR_CHelper ? TR::RealRegister::gr4 : TR::RealRegister::gr3;
      objectReg = conditions->searchPreConditionRegister(static_cast<TR::RealRegister::RegNum>(firstArgReg + 1));
      castClassReg = conditions->searchPreConditionRegister(firstArgReg);
      scratch1Reg = conditions->searchPreConditionRegister(static_cast<TR::RealRegister::RegNum>(firstArgReg + 2));
      scratch2Reg = conditions->searchPreConditionRegister(static_cast<TR::RealRegister::RegNum>(firstArgReg + 4));
      scratch3Reg = conditions->searchPreConditionRegister(static_cast<TR::RealRegister::RegNum>(firstArgReg + 6));
      objClassReg = conditions->searchPreConditionRegister(static_cast<TR::RealRegister::RegNum>(firstArgReg + 3));
      crReg = conditions->searchPreConditionRegister(TR::RealRegister::cr0);
      if (!crReg)
         {
         // This is another hack to compensate for skipping the preserving of CCRs for helper calls in linkage code.
         // This hack wouldn't be necessary if this code wasn't being lazy and re-using the dependencies
         // set up by the direct call evaluator, but alas...
         // Since we are not preserving CCRs and this code previously expected to find a CCR in the call instruction's
         // dependency conditions we allocate a new virtual here and stick it in the dependencies, making sure to
         // repeat everything that is normally done for virtual regs when they are referenced by a generated instruction,
         // which just means we record two uses to correspond to the fact that we added them to the pre/post deps of the
         // call.
         crReg = nonFixedDependency(conditions, NULL, &depIndex, TR_CCR, false, cg);
         stopUsingHelperCallCR = true;
         TR::Instruction *callInstruction = scratch1Reg->getStartOfRange();
         callInstruction->useRegister(crReg);
         callInstruction->useRegister(crReg);
         }

      if (depNode != NULL)
         {
         cg->evaluate(depNode);
         deps = generateRegisterDependencyConditions(cg, depNode, 0, &iCursor);
         }

      if (testEqualClass || testCache || testCastClassIsSuper)
         {
         if (needResult)
            {
            resultReg = conditions->searchPreConditionRegister(static_cast<TR::RealRegister::RegNum>(firstArgReg + 5));
            iCursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, 0, iCursor);
            }
         if (!objectNode->isNonNull())
            {
            iCursor = genNullTest(objectNode, objectReg, crReg, scratch1Reg, iCursor, cg);
            iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, res0Label, crReg, iCursor);
            }
         }
      }
   else
      {
      bool earlyEval = (castClassNode->getOpCodeValue() != TR::loadaddr || castClassNode->getReferenceCount() > 1) ? true : false;

      bool objInDep = false, castClassInDep = false;
      if (depNode != NULL)
         {
         int32_t i1;

         TR::Node *grNode;
         cg->evaluate(depNode);
         deps = generateRegisterDependencyConditions(cg, depNode, 7);

         for (i1 = 0; i1 < numDep; i1++)
            {
            grNode = depNode->getChild(i1);
            if (grNode->getOpCodeValue() == TR::PassThrough)
               grNode = grNode->getFirstChild();
            if (grNode == objectNode)
               objInDep = true;
            if (grNode == castClassNode)
               castClassInDep = true;
            }
         }

      if (deps == NULL)
         {
         conditions = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(7, 7, cg->trMemory());
         }
      else
         {
         conditions = deps;
         }

      crReg = nonFixedDependency(conditions, NULL, &depIndex, TR_CCR, false, cg);

      if (needResult)
         resultReg = nonFixedDependency(conditions, NULL, &depIndex, TR_GPR, false, cg);

      objClassReg = nonFixedDependency(conditions, NULL, &depIndex, TR_GPR, true, cg);

      objectReg = cg->evaluate(objectNode);

      if (!objInDep)
         objectReg = nonFixedDependency(conditions, objectReg, &depIndex, TR_GPR, true, cg);

      if (earlyEval)
         {
         castClassReg = cg->evaluate(castClassNode);
         castClassReg = nonFixedDependency(conditions, castClassReg, &depIndex, TR_GPR, true, cg);
         }

      if (needResult)
         generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, 0);

      if (!objectNode->isNonNull())
         {
         genNullTest(objectNode, objectReg, crReg, objClassReg, NULL, cg);
         generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, res0Label, crReg);
         }

      if (!earlyEval)
         {
         castClassReg = cg->evaluate(castClassNode);
         castClassReg = nonFixedDependency(conditions, castClassReg, &depIndex, TR_GPR, true, cg);
         }

      if (testCastClassIsSuper)
         {
         scratch1Reg = nonFixedDependency(conditions, NULL, &depIndex, TR_GPR, true, cg);
         scratch2Reg = nonFixedDependency(conditions, NULL, &depIndex, TR_GPR, true, cg);
         cg->stopUsingRegister(scratch1Reg);
         cg->stopUsingRegister(scratch2Reg);
         }

      iCursor = cg->getAppendInstruction();

      generateDepLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel, conditions);

      cg->decReferenceCount(objectNode);
      cg->decReferenceCount(castClassNode);
      cg->stopUsingRegister(crReg);
      cg->stopUsingRegister(objClassReg);
      }

   if (testEqualClass || testCache || testCastClassIsSuper)
      {
      if (TR::Compiler->om.compressObjectReferences())
        // read only 32 bits
         iCursor = generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, objClassReg,
               TR::MemoryReference::createWithDisplacement(cg, objectReg,
                     (int32_t) TR::Compiler->om.offsetOfObjectVftField(), 4),
               iCursor);
      else
         iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, objClassReg,
               TR::MemoryReference::createWithDisplacement(cg, objectReg, (int32_t) TR::Compiler->om.offsetOfObjectVftField(), TR::Compiler->om.sizeofReferenceAddress()), iCursor);
      iCursor = TR::TreeEvaluator::generateVFTMaskInstruction(cg, node, objClassReg, iCursor);
      }

   if (testEqualClass)
      {
      iCursor = generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, crReg, objClassReg, castClassReg, iCursor);

      if (testCache || testCastClassIsSuper)
         {
         if (needResult)
            {
            trueLabel = generateLabelSymbol(cg);
            iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, trueLabel, crReg, iCursor);
            }
         else
            {
            iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, res1Label, crReg, iCursor);
            }
         }
      else if (needHelperCall)
         {
         if (needResult)
            {
            iCursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, 1, iCursor);
            }
         iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, res1Label, crReg, iCursor);

         bool castClassIsReferenceArray = (comp->isOptServer()) && (!castClassNode->getSymbolReference()->isUnresolved())
               && (castClassNode->getSymbolReference() == comp->getSymRefTab()->findVftSymbolRef()) && (castClassNode->getNumChildren() == 1)
               && (castClassNode->getFirstChild()->getOpCodeValue() == TR::anewarray);
         if (castClassIsReferenceArray)
            {
            TR_OpaqueClassBlock * jlobjectclassBlock = fej9->getSystemClassFromClassName("java/lang/Object", 16);
            J9Class* jlobjectarrayclassBlock = jlobjectclassBlock ? (J9Class*)fej9->getArrayClassFromComponentClass((TR_OpaqueClassBlock*)jlobjectclassBlock) : NULL;
            if (jlobjectarrayclassBlock != NULL)
               {
               iCursor = loadAddressConstant(cg, comp->compileRelocatableCode(), node, (intptr_t) (jlobjectarrayclassBlock), scratch1Reg, iCursor);
               iCursor = generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, crReg, objClassReg, scratch1Reg, iCursor);
               if (needResult)
                  {
                  iCursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, 0, iCursor);
                  }
               iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, res0Label, crReg, iCursor);
               }
            }
         }
      else
         {
         iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, res0Label, crReg, iCursor);
         if (needResult)
            {
            iCursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, 1, iCursor);
            }

         if (branchOn1)
            {
            iCursor = generateLabelInstruction(cg, TR::InstOpCode::b, node, res1Label, iCursor);
            }
         }
      }

   if (testCache && (!comp->compileRelocatableCode() || comp->getOption(TR_UseSymbolValidationManager)))
      {
      // The cached value could have been from a previously successful checkcast or instanceof.
      // An answer of 0 in the low order bit indicates 'success' (the cast or instanceof was successful).
      // An answer of 1 in the lower order bit indicates 'failure' (the cast would have thrown an exception, instanceof would have been unsuccessful)
      // Because of this, we can just do a simple load and compare of the 2 class pointers. If it succeeds, the low order bit
      // must be off (success) from a previous checkcast or instanceof. If the low order bit is on, it is guaranteed not to
      // compare and we will take the slow path.
      // Generate inline test for instanceOf
      // Don't inline test when AOT && non-SVM compilation
      genInlineTest(node, castClassAddr, guessClassArray, num_PICS, objClassReg, resultReg, crReg, scratch1Reg, scratch2Reg, false, needResult, res0Label, res1Label, doneLabel,
            callLabel, testCastClassIsSuper, iCursor, cg);
      }

   if (testCastClassIsSuper)
      {
      bool depthInReg2 = false;

      TR::StaticSymbol    *castClassSym = NULL;
      if (castClassSymRef && !castClassSymRef->isUnresolved())
         castClassSym = castClassSymRef->getSymbol()->getStaticSymbol();

      TR_OpaqueClassBlock * clazz = NULL;
      if (castClassSym)
         clazz = (TR_OpaqueClassBlock *) castClassSym->getStaticAddress();

      if(clazz)
         castClassDepth = (int32_t)TR::Compiler->cls.classDepthOf(clazz);

      if (castClassDepth == -1)
         {
         iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, scratch2Reg,
            TR::MemoryReference::createWithDisplacement(cg, castClassReg, offsetof(J9Class, classDepthAndFlags), TR::Compiler->om.sizeofReferenceAddress()), iCursor);
         iCursor = generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, scratch2Reg, scratch2Reg, 0, J9AccClassDepthMask, iCursor);
         depthInReg2 = true;
         }

      iCursor = genTestIsSuper(node, objClassReg, castClassReg, crReg, scratch1Reg, scratch2Reg, castClassDepth, res0Label, iCursor, cg, depthInReg2);
      iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, res0Label, crReg, iCursor);

      if (trueLabel == NULL)
         {
         if (needResult)
            {
            iCursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, 1, iCursor);
            }
         }
      else
         {
         iCursor = generateLabelInstruction(cg, TR::InstOpCode::label, node, trueLabel, iCursor);
         iCursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, 1, iCursor);
         }

      if (branchOn1)
         {
         iCursor = generateLabelInstruction(cg, TR::InstOpCode::b, node, res1Label, iCursor);
         }
      }
   else if (testCache)
      {
      if (trueLabel == NULL)
         {
         if (needResult)
            {
            iCursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, 1, iCursor);
            }
         }
      else
         {
         iCursor = generateLabelInstruction(cg, TR::InstOpCode::label, node, trueLabel, iCursor);
         iCursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, 1, iCursor);
         }
      }

   if (needHelperCall)
      {
      TR::Register *callResult = node->getRegister();
      TR::RegisterDependencyConditions *newDeps;

      if (resultReg == NULL)
         {
         resultReg = callResult;
         }
      else
         {
         generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, resultReg, callResult);
         }

      if (isVMifInstanceOf)
         {
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpli4, node, crReg, resultReg, 0);
         }

      if (depNode != NULL)
         {
         newDeps = conditions->cloneAndFix(cg, deps);
         }
      else
         {
         newDeps = conditions->cloneAndFix(cg);
         }

      if (isVMifInstanceOf)
         {
         if (branchOn1)
            {
            generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, res1Label, crReg);
            }
         else
            {
            generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, res0Label, crReg);
            }
         }

      // Just fall through
      // generateLabelInstruction(cg, TR::InstOpCode::b, node, doneLabel);

      // Done
      generateDepLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel, newDeps);

      if (stopUsingHelperCallCR)
         cg->stopUsingRegister(crReg);

      // We have to revive the resultReg here. Should revisit to see how to use registers better
      // (e.g., reducing the chance of moving around).
      if (resultReg != callResult)
         reviveResultRegister(resultReg, callResult, cg);
      }

   if (depNode != NULL)
      cg->decReferenceCount(depNode);

   return resultReg;
   }

static TR::Register *
reservationLockEnter(TR::Node *node, int32_t lwOffset, TR::CodeGenerator *cg, TR::RegisterDependencyConditions *conditions, TR::Register *objReg, TR::Register *monitorReg, TR::Register *valReg, TR::Register *tempReg, TR::Register *cndReg, TR::LabelSymbol *callLabel)
   {
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());
   TR::LabelSymbol *resLabel, *doneLabel, *callReturnLabel, *loopLabel, *reserved_checkLabel;
   int32_t lockSize;
   TR::Compilation * comp = cg->comp();

   TR::Register *metaReg = cg->getMethodMetaDataRegister();

   resLabel = generateLabelSymbol(cg);
   loopLabel = generateLabelSymbol(cg);
   reserved_checkLabel = generateLabelSymbol(cg);
   doneLabel = generateLabelSymbol(cg);
   callReturnLabel = generateLabelSymbol(cg);

   TR::TreeEvaluator::isPrimitiveMonitor(node, cg);
   bool isPrimitive = node->isPrimitiveLockedRegion();
   lockSize = comp->target().is64Bit() && !fej9->generateCompressedLockWord() ? 8 : 4;

   /*
    * PRIMITIVE lockReservation enter sequence:            Non-Primitive lockReservation enter sequence:
    *    ld/lwz monitorReg, lwOffset(objReg)                  ld/lwz monitorReg, lwOffset(objReg)
    *    ori    valReg, metaReg, LOCK_RESERVATION_BIT         ori    valReg, metaReg, LOCK_RESERVATION_BIT
    *    cmpl   cr0, monitorReg, valReg                       cmpl   cr0, monitorReg, valReg
    *    beq    doneLabel                                     bne    resLabel                          <--- Diff
    *                                                         addi   tempReg, monitorReg, LOCK_INC_DEC_VALUE <--- Diff
    *                                                         st     tempReg, lwOffset(objReg)         <--- Diff
    *                                                         b      doneLabel
    * resLabel(startOOLLabel):
    *    cmpli  cr0, monitorReg, 0                            cmpli  cr0, monitorReg, 0
    *    bne    reserved_checkLabel                           bne    reserved_checkLabel
    *    li     tempReg, lwOffset                             li     tempReg, lwOffset
    *                                                         addi   valReg, metaReg, RES+INC          <--- Diff
    * loopLabel:
    *    larx   monitorReg, [objReg, tempReg]                 larx   monitorReg, [objReg, tempReg]
    *    cmpli  cr0, monitorReg, 0                            cmpli  cr0, monitorReg, 0
    *    bne    callLabel                                     bne    callLabel
    *    stcx.  valReg, [objReg, tempReg]                     stcx.  valReg, [objReg, tempReg]
    *    bne    loopLabel                                     bne    loopLabel
    *    isync                                                isync
    *    b      doneLabel                                     b      doneLabel
    * reserved_checkLabel:
    *    li     tempReg, PRIMITIVE_ENTER_MASK                 li     tempReg, NON_PRIMITIVE_ENTER_MASK <--- Diff
    *    andc   tempReg, monitorReg, tempReg                  andc   tempReg, monitorReg, tempReg
    *                                                         addi   valReg, metaReg, RES              <--- Diff
    *    cmpl   cr0, tempReg, valReg                          cmpl   cr0, tempReg, valReg
    *    bne    callLabel                                     bne    callLabel
    *                                                         addi   monitorReg, monitorReg, INC       <--- Diff
    *                                                         st     monitorReg, [objReg, lwOffset]    <--- Diff
    * doneLabel:
    * #if JAVA_SPEC_VERSION >= 19
    *    ld/lwz tempReg, OwnedMonitorCountOffset(metaReg)     ld/lwz tempReg, OwnedMonitorCountOffset(metaReg)
    *    addi   tempReg, tempReg, 1                           addi   tempReg, tempReg, 1
    *    st     tempReg, OwnedMonitorCountOffset(metaReg)     st     tempReg, OwnedMonitorCountOffset(metaReg)
    * #endif //JAVA_SPEC_VERSION >= 19
    * callReturnLabel:
    * === OUT OF LINE ===
    * callLabel:
    *    bl     jitMonitorEntry
    *    b      callReturnLabel
    */

   generateTrg1MemInstruction(cg, lockSize == 8 ? TR::InstOpCode::ld : TR::InstOpCode::lwz, node, monitorReg, TR::MemoryReference::createWithDisplacement(cg, objReg, lwOffset, lockSize));
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::ori, node, valReg, metaReg, LOCK_RESERVATION_BIT);
   generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, cndReg, monitorReg, valReg);

   if (!isPrimitive)
      {
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, resLabel, cndReg);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, tempReg, monitorReg, LOCK_INC_DEC_VALUE);
      generateMemSrc1Instruction(cg, lockSize == 8 ? TR::InstOpCode::std : TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, objReg, lwOffset, lockSize), tempReg);
      generateLabelInstruction(cg, TR::InstOpCode::b, node, doneLabel);
      }
   else
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, cndReg);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, resLabel);
   generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpli, node, cndReg, monitorReg, 0);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, reserved_checkLabel, cndReg);
   generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, tempReg, lwOffset);
   if (!isPrimitive)
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, valReg, metaReg, LOCK_RESERVATION_BIT | LOCK_INC_DEC_VALUE);
   generateLabelInstruction(cg, TR::InstOpCode::label, node, loopLabel);
   generateTrg1MemInstruction(cg, lockSize == 8 ? TR::InstOpCode::ldarx : TR::InstOpCode::lwarx,
                              PPCOpProp_LoadReserveExclusiveAccess, node, monitorReg, TR::MemoryReference::createWithIndexReg(cg, objReg, tempReg, lockSize));
   generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpli, node, cndReg, monitorReg, 0);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, callLabel, cndReg);
   generateMemSrc1Instruction(cg, lockSize == 8 ? TR::InstOpCode::stdcx_r : TR::InstOpCode::stwcx_r,
                              node, TR::MemoryReference::createWithIndexReg(cg, objReg, tempReg, lockSize), valReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, loopLabel, cndReg);
   generateInstruction(cg, TR::InstOpCode::isync, node);
   generateLabelInstruction(cg, TR::InstOpCode::b, node, doneLabel);
   generateLabelInstruction(cg, TR::InstOpCode::label, node, reserved_checkLabel);
   generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, tempReg, isPrimitive ? LOCK_RES_PRIMITIVE_ENTER_MASK : LOCK_RES_NON_PRIMITIVE_ENTER_MASK);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::andc, node, tempReg, monitorReg, tempReg);
   if (!isPrimitive)
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, valReg, metaReg, LOCK_RESERVATION_BIT);
   generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, cndReg, tempReg, valReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, callLabel, cndReg);
   if (!isPrimitive)
      {
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, monitorReg, monitorReg, LOCK_INC_DEC_VALUE);
      generateMemSrc1Instruction(cg, lockSize == 8 ? TR::InstOpCode::std : TR::InstOpCode::stw,
                                 node, TR::MemoryReference::createWithDisplacement(cg, objReg, lwOffset & 0x0000FFFF, lockSize), monitorReg);
      }

   generateLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel);
#if JAVA_SPEC_VERSION >= 19
      /*
       * Adjust J9VMThread ownedMonitorCount for execution paths that do not
       * go out of line to acquire the lock. It is safe and efficient to do
       * this unconditionally. There is no need to check for overflow.
       */
      generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, tempReg, TR::MemoryReference::createWithDisplacement(cg, metaReg, fej9->thisThreadGetOwnedMonitorCountOffset(), TR::Compiler->om.sizeofReferenceAddress()));
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, tempReg, tempReg, 1);
      generateMemSrc1Instruction(cg, TR::InstOpCode::Op_st, node, TR::MemoryReference::createWithDisplacement(cg, metaReg, fej9->thisThreadGetOwnedMonitorCountOffset(), TR::Compiler->om.sizeofReferenceAddress()), tempReg);
#endif //JAVA_SPEC_VERSION >= 19

   TR_PPCOutOfLineCodeSection *outlinedHelperCall = new (cg->trHeapMemory()) TR_PPCOutOfLineCodeSection(node, TR::call, NULL, callLabel, callReturnLabel, cg);
   cg->getPPCOutOfLineCodeSectionList().push_front(outlinedHelperCall);

   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, callReturnLabel, conditions);
   callReturnLabel->setEndInternalControlFlow();

   conditions->stopUsingDepRegs(cg, objReg);
   cg->decReferenceCount(node->getFirstChild());
   return NULL;
   }

static TR::Register *
reservationLockExit(TR::Node *node, int32_t lwOffset, TR::CodeGenerator *cg, TR::RegisterDependencyConditions *conditions, TR::Register *objReg, TR::Register *monitorReg, TR::Register *valReg, TR::Register *tempReg, TR::Register *cndReg, TR::LabelSymbol *callLabel)
   {
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());
   TR::LabelSymbol *resLabel, *doneLabel, *callReturnLabel;
   int32_t lockSize;
   TR::Compilation *comp = cg->comp();

   TR::Register *metaReg = cg->getMethodMetaDataRegister();

   resLabel = generateLabelSymbol(cg);
   doneLabel = generateLabelSymbol(cg);
   callReturnLabel = generateLabelSymbol(cg);

   bool isPrimitive = node->isPrimitiveLockedRegion();
   lockSize = comp->target().is64Bit() && !fej9->generateCompressedLockWord() ? 8 : 4;

   /*
    * PRIMITIVE reservationLock exit sequence              Non-PRIMITIVE reservationLock exit sequence
    *    ld/lwz monitorReg, [objReg, lwOffset]             ld/lwz monitorReg, [objReg, lwOffset]
    *    andi_r tempReg, monitorReg, PRIMITIVE_EXIT_MASK   ori    tempReg, metaReg, RES+INC              <-- Diff
    *    cmpli  cndReg, tempReg, LOCK_RESERVATION_BIT      cmpl   cndReg, monitorReg, tempReg            <-- Diff
    *    bne    resLabel                                   bne    resLabel                               <-- Diff
    *                                                      addi   valReg, metaReg, LOCK_RESERVATION_BIT  <-- Diff
    *                                                      st     valReg, [objReg, lwOffset]             <-- Diff
    *                                                      b      doneLabel                              <-- Diff
    * resLabel(startOOLLabel):
    *    li     tempReg, RES_OWNING_COMPLEMENT             li     tempReg, RES_OWNING_COMPLEMENT
    *    andc   tempReg, monitorReg, tempReg               andc   tempReg, monitorReg, tempReg
    *    addi   valReg, metaReg, RES_BIT                   addi   valReg, metaReg, RES_BIT
    *    cmpl   cndReg, tempReg, valReg                    cmpl   cndReg, tempReg, valReg
    *    bne    callLabel                                  bne    callLabel
    *    andi_r tempReg, monitorReg, RECURSION_MASK        andi_r tempReg, monitorReg, NON_PRIMITIVE_EXIT_MASK <-- Diff
    *    bne    doneLabel                                  beq    callLabel                                    <-- Diff
    *    addi   monitorReg, monitorReg, INC                addi   monitorReg, monitorReg, -INC                 <-- Diff
    *    st     monitorReg, [objReg, lwOffset]             st     monitorReg, [objReg, lwOffset]
    *                                                      b      doneLabel                                    <-- Diff
    * doneLabel:
    * #if JAVA_SPEC_VERSION >= 19
    *    ld/lwz tempReg, OwnedMonitorCountOffset(metaReg)  ld/lwz tempReg, OwnedMonitorCountOffset(metaReg)
    *    addi   tempReg, tempReg, -1                       addi   tempReg, tempReg, -1
    *    st     tempReg, OwnedMonitorCountOffset(metaReg)  st     tempReg, OwnedMonitorCountOffset(metaReg)
    * #endif //JAVA_SPEC_VERSION >= 19
    * callReturnLabel:
    * === OUT OF LINE ===
    * callLabel:
    *    bl     jitMonitorExit
    *    b      callReturnLabel
    */

   generateTrg1MemInstruction(cg, lockSize == 8 ? TR::InstOpCode::ld : TR::InstOpCode::lwz, node, monitorReg, TR::MemoryReference::createWithDisplacement(cg, objReg, lwOffset, lockSize));
   if (!node->isPrimitiveLockedRegion())
      {
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::ori, node, tempReg, metaReg, LOCK_RESERVATION_BIT + LOCK_INC_DEC_VALUE);
      generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, cndReg, monitorReg, tempReg);
      }
   else
      {
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, tempReg, monitorReg, LOCK_RES_PRIMITIVE_EXIT_MASK);
      generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpli, node, cndReg, tempReg, LOCK_RESERVATION_BIT);
      }

   if (!isPrimitive)
      {
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, resLabel, cndReg);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, valReg, metaReg, LOCK_RESERVATION_BIT);
      generateMemSrc1Instruction(cg, lockSize == 8 ? TR::InstOpCode::std : TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, objReg, lwOffset, lockSize), valReg);
      generateLabelInstruction(cg, TR::InstOpCode::b, node, doneLabel);
      }
   else
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, resLabel, cndReg);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, resLabel);
   generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, tempReg, LOCK_RES_OWNING_COMPLEMENT);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::andc, node, tempReg, monitorReg, tempReg);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, valReg, metaReg, LOCK_RESERVATION_BIT);
   generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, cndReg, tempReg, valReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, callLabel, cndReg);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, tempReg, monitorReg, isPrimitive ? OBJECT_HEADER_LOCK_RECURSION_MASK : LOCK_RES_NON_PRIMITIVE_EXIT_MASK);
   if (isPrimitive)
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, doneLabel, cndReg);
   else
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, callLabel, cndReg);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, monitorReg, monitorReg, isPrimitive ? LOCK_INC_DEC_VALUE : -LOCK_INC_DEC_VALUE);
   generateMemSrc1Instruction(cg, lockSize == 8 ? TR::InstOpCode::std : TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, objReg, lwOffset, lockSize), monitorReg);
   if (!isPrimitive)
      generateLabelInstruction(cg, TR::InstOpCode::b, node, doneLabel);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel);
#if JAVA_SPEC_VERSION >= 19
      /*
       * Adjust J9VMThread ownedMonitorCount for execution paths that do not
       * go out of line to release the lock. It is safe and efficient to do
       * this unconditionally. There is no need to check for underflow.
       */
      generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, tempReg, TR::MemoryReference::createWithDisplacement(cg, metaReg, fej9->thisThreadGetOwnedMonitorCountOffset(), TR::Compiler->om.sizeofReferenceAddress()));
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, tempReg, tempReg, -1);
      generateMemSrc1Instruction(cg, TR::InstOpCode::Op_st, node, TR::MemoryReference::createWithDisplacement(cg, metaReg, fej9->thisThreadGetOwnedMonitorCountOffset(), TR::Compiler->om.sizeofReferenceAddress()), tempReg);
#endif //JAVA_SPEC_VERSION >= 19

   TR_PPCOutOfLineCodeSection *outlinedHelperCall = new (cg->trHeapMemory()) TR_PPCOutOfLineCodeSection(node, TR::call, NULL, callLabel, callReturnLabel, cg);
   cg->getPPCOutOfLineCodeSectionList().push_front(outlinedHelperCall);

   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, callReturnLabel, conditions);
   callReturnLabel->setEndInternalControlFlow();

   conditions->stopUsingDepRegs(cg, objReg);
   cg->decReferenceCount(node->getFirstChild());
   return NULL;
   }

TR::Register *J9::Power::TreeEvaluator::VMmonexitEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());
   int32_t lwOffset = fej9->getByteOffsetToLockword((TR_OpaqueClassBlock *) cg->getMonClass(node));
   TR::Compilation *comp = cg->comp();
   TR_YesNoMaybe isMonitorValueBasedOrValueType = cg->isMonitorValueBasedOrValueType(node);
   bool unconditionalSync = false;

   // WARNING:
   // If there is an AllocationFence directly above this monExit we will not have emitted an
   // lwsync for the fence and now must ensure that an lwsync is always executed for this monexit.
   // When unconditionalSync is true, all paths through this method must emit an unconditional 'lwsync'!
   if (cg->getCurrentEvaluationTreeTop()->getPrevTreeTop()->getNode()->getOpCodeValue() == TR::allocationFence)
      unconditionalSync = true;

   if (comp->getOption(TR_FullSpeedDebug) ||
         (isMonitorValueBasedOrValueType == TR_yes) ||
         comp->getOption(TR_DisableInlineMonExit))
      {
      TR::ILOpCodes opCode = node->getOpCodeValue();
      TR::Node::recreate(node, TR::call);
      TR::Register *targetRegister = directCallEvaluator(node, cg);
      TR::Node::recreate(node, opCode);
      if (comp->target().isSMP() && unconditionalSync == true)
         generateInstruction(cg, TR::InstOpCode::lwsync, node);
      return targetRegister;
      }

   int32_t numDeps = 6;

   TR::Node *objNode = node->getFirstChild();
   TR::Register *objReg = cg->evaluate(objNode);

   TR::Register *monitorReg = cg->allocateRegister();
   TR::Register *tempReg = cg->allocateRegister();
   TR::Register *threadReg = cg->allocateRegister();

   TR::Register *objectClassReg = cg->allocateRegister();
   TR::Register *lookupOffsetReg = NULL;

   TR::Register *condReg = cg->allocateRegister(TR_CCR);
   TR::Register *metaReg = cg->getMethodMetaDataRegister();

   TR::Register *baseReg = objReg;

   if (lwOffset <= 0)
      {
      if (comp->getOption(TR_EnableMonitorCacheLookup))
         {
         lookupOffsetReg = cg->allocateRegister();
         numDeps++;
         }
      }

   TR::RegisterDependencyConditions *conditions;
   conditions = createConditionsAndPopulateVSXDeps(cg, numDeps);

   TR::addDependency(conditions, objReg, TR::RealRegister::NoReg, TR_GPR, cg);
   conditions->getPreConditions()->getRegisterDependency(conditions->getAddCursorForPre() - 1)->setExcludeGPR0();
   conditions->getPostConditions()->getRegisterDependency(conditions->getAddCursorForPost() - 1)->setExcludeGPR0();

   TR::addDependency(conditions, monitorReg, TR::RealRegister::NoReg, TR_GPR, cg);
   conditions->getPreConditions()->getRegisterDependency(conditions->getAddCursorForPre() - 1)->setExcludeGPR0();
   conditions->getPostConditions()->getRegisterDependency(conditions->getAddCursorForPost() - 1)->setExcludeGPR0();

   TR::addDependency(conditions, tempReg, TR::RealRegister::NoReg, TR_GPR, cg);

   TR::addDependency(conditions, threadReg, TR::RealRegister::NoReg, TR_GPR, cg);

   TR::addDependency(conditions, objectClassReg, TR::RealRegister::NoReg, TR_GPR, cg);
   conditions->getPreConditions()->getRegisterDependency(conditions->getAddCursorForPre() - 1)->setExcludeGPR0();
   conditions->getPostConditions()->getRegisterDependency(conditions->getAddCursorForPost() - 1)->setExcludeGPR0();

   if (lookupOffsetReg)
      {
      TR::addDependency(conditions, lookupOffsetReg, TR::RealRegister::NoReg, TR_GPR, cg);
      conditions->getPreConditions()->getRegisterDependency(conditions->getAddCursorForPre() - 1)->setExcludeGPR0();
      conditions->getPostConditions()->getRegisterDependency(conditions->getAddCursorForPost() - 1)->setExcludeGPR0();
      }

   TR::addDependency(conditions, condReg, TR::RealRegister::cr0, TR_CCR, cg);

   TR::LabelSymbol *callLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *monitorLookupCacheLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *fallThruFromMonitorLookupCacheLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *startLabel = generateLabelSymbol(cg);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, startLabel, NULL);
   startLabel->setStartInternalControlFlow();

   //If object is not known to be value type or value based class at compile time, check at run time
   if (isMonitorValueBasedOrValueType == TR_maybe)
      {
      generateCheckForValueMonitorEnterOrExit(node, callLabel, objReg, objectClassReg, tempReg, threadReg, condReg, cg, J9_CLASS_DISALLOWS_LOCKING_FLAGS);
      }

   bool simpleLocking = false;

   if (lwOffset <= 0)
      {
      generateLoadJ9Class(node, objectClassReg, objReg, cg);

      int32_t offsetOfLockOffset = offsetof(J9Class, lockOffset);
      TR::MemoryReference *tempMR = TR::MemoryReference::createWithDisplacement(cg, objectClassReg, offsetOfLockOffset, TR::Compiler->om.sizeofReferenceAddress());
      generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, objectClassReg, tempMR);

      generateTrg1Src1ImmInstruction(cg, comp->target().is64Bit() ? TR::InstOpCode::cmpi8 : TR::InstOpCode::cmpi4, node, condReg, objectClassReg, 0);

      if (comp->getOption(TR_EnableMonitorCacheLookup))
         {
         lwOffset = 0;
         generateConditionalBranchInstruction(cg, TR::InstOpCode::ble, node, monitorLookupCacheLabel, condReg);
         generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, objectClassReg, objReg, objectClassReg);
         generateLabelInstruction(cg, TR::InstOpCode::b, node, fallThruFromMonitorLookupCacheLabel);

         generateLabelInstruction(cg, TR::InstOpCode::label, node, monitorLookupCacheLabel);

         int32_t offsetOfMonitorLookupCache = offsetof(J9VMThread, objectMonitorLookupCache);

         generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, lookupOffsetReg, objReg);

         int32_t t = trailingZeroes(TR::Compiler->om.getObjectAlignmentInBytes());

         if (comp->target().is64Bit())
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::sradi, node, lookupOffsetReg, lookupOffsetReg, t);
         else
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::srawi, node, lookupOffsetReg, lookupOffsetReg, t);

         J9JavaVM * jvm = fej9->getJ9JITConfig()->javaVM;

         if (comp->target().is64Bit())
         simplifyANDRegImm(node, lookupOffsetReg, lookupOffsetReg, (int64_t) J9VMTHREAD_OBJECT_MONITOR_CACHE_SIZE - 1, cg, objNode);
         else
         simplifyANDRegImm(node, lookupOffsetReg, lookupOffsetReg, J9VMTHREAD_OBJECT_MONITOR_CACHE_SIZE - 1, cg, objNode);

         if (comp->target().is64Bit())
         generateShiftLeftImmediateLong(cg, node, lookupOffsetReg, lookupOffsetReg, trailingZeroes((int32_t) TR::Compiler->om.sizeofReferenceField()));
         else
         generateShiftLeftImmediate(cg, node, lookupOffsetReg, lookupOffsetReg, trailingZeroes((int32_t) TR::Compiler->om.sizeofReferenceField()));

         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, lookupOffsetReg, lookupOffsetReg, offsetOfMonitorLookupCache);

         generateTrg1MemInstruction(cg, (comp->target().is64Bit() && fej9->generateCompressedLockWord()) ? TR::InstOpCode::lwz :TR::InstOpCode::Op_load, node, objectClassReg,
               TR::MemoryReference::createWithIndexReg(cg, metaReg, lookupOffsetReg, TR::Compiler->om.sizeofReferenceField()));

         generateTrg1Src1ImmInstruction(cg, comp->target().is64Bit() ? TR::InstOpCode::cmpi8 : TR::InstOpCode::cmpi4, node, condReg, objectClassReg, 0);
         generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, callLabel, condReg);

         int32_t offsetOfMonitor = offsetof(J9ObjectMonitor, monitor);
         generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, lookupOffsetReg,
               TR::MemoryReference::createWithDisplacement(cg, objectClassReg, offsetOfMonitor, TR::Compiler->om.sizeofReferenceAddress()));

         int32_t offsetOfUserData = offsetof(J9ThreadAbstractMonitor, userData);
         generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, lookupOffsetReg,
               TR::MemoryReference::createWithDisplacement(cg, lookupOffsetReg, offsetOfUserData, TR::Compiler->om.sizeofReferenceAddress()));

         generateTrg1Src2Instruction(cg, comp->target().is64Bit() ? TR::InstOpCode::cmp8 : TR::InstOpCode::cmp4, node, condReg, lookupOffsetReg, objReg);
         generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, callLabel, condReg);

         int32_t offsetOfAlternateLockWord = offsetof(J9ObjectMonitor, alternateLockword);

         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, objectClassReg, objectClassReg, offsetOfAlternateLockWord);

         generateLabelInstruction(cg, TR::InstOpCode::label, node, fallThruFromMonitorLookupCacheLabel);
         }
      else
         {
         generateConditionalBranchInstruction(cg, TR::InstOpCode::ble, node, callLabel, condReg);
         generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, objectClassReg, objReg, objectClassReg);
         }

      simpleLocking = true;
      lwOffset = 0;
      baseReg = objectClassReg;
      }

   /* If the -XX:-GlobalLockReservation option is set, try to use the original aggressive reserved locking code path. */
   if (!fej9->isEnableGlobalLockReservationSet())
      {
      bool reserveLocking = false, normalLockWithReservationPreserving = false;

      if (!simpleLocking && comp->getOption(TR_ReservingLocks))
         TR::TreeEvaluator::evaluateLockForReservation(node, &reserveLocking, &normalLockWithReservationPreserving, cg);

      if (reserveLocking)
         {
         if (comp->target().isSMP() && unconditionalSync == true)
            generateInstruction(cg, TR::InstOpCode::lwsync, node);
         return reservationLockExit(node, lwOffset, cg, conditions, baseReg, monitorReg, threadReg, tempReg, condReg, callLabel);
         }
      }

   int32_t lockSize;
   TR::InstOpCode::Mnemonic loadOpCode, storeOpCode, reservedLoadOpCode, conditionalStoreOpCode, compareLogicalOpCode, compareLogicalImmOpCode;

   if (comp->target().is64Bit() && !fej9->generateCompressedLockWord())
      {
      lockSize = 8;
      loadOpCode = TR::InstOpCode::ld;
      storeOpCode = TR::InstOpCode::std;
      reservedLoadOpCode = TR::InstOpCode::ldarx;
      conditionalStoreOpCode = TR::InstOpCode::stdcx_r;
      compareLogicalOpCode = TR::InstOpCode::cmpl8;
      compareLogicalImmOpCode = TR::InstOpCode::cmpli8;
      }
   else
      {
      lockSize = 4;
      loadOpCode = TR::InstOpCode::lwz;
      storeOpCode = TR::InstOpCode::stw;
      reservedLoadOpCode = TR::InstOpCode::lwarx;
      conditionalStoreOpCode = TR::InstOpCode::stwcx_r;
      compareLogicalOpCode = TR::InstOpCode::cmpl4;
      compareLogicalImmOpCode = TR::InstOpCode::cmpli4;
      }

   /*
    * Read only locks do not support either the Reserved bit nor the Learning bit.
    * They are disabled until the code path is updated.
    */
   if (true || !ppcSupportsReadMonitors || !node->isReadMonitor())
      {
      /*
       * JIT unlocking fast path checks:
       * 1. Check if lockword matches TID and the lower 8 bits are clear. If so, clear lockword to unlock the object.
       * 2. Check if TID matches, Learning bit is clear, and Inflated bit is clear. If not, call VM.
       * 3. Check if RC field is at least 1. If so, decrement RC.
       * 4. All fast path checks failed so go to VM.
       *
       * Check 1 catches Flat-Locked (non-nested, FLC bit clear).
       * Check 2 catches New-PreLearning, New-AutoReserve, Learning-Unlocked, Learning-Locked, Flat-Unlocked and Inflated and sends them to the VM.
       * Check 3 catches Flat-Locked (nested case) and Reserved-Locked.
       * Check 4 catches Reserved-Unlocked and Flat-Locked (non-nested case, FLC bit set) and sends it to the VM.
       *
       *
       *    ld/lwz monitorReg, lwOffset(baseReg)
       *    li     tempReg, 0
       *    cmpl   cr0, monitorReg, metaReg
       *    lwsync <if unconditionalSync>
       *    bne    decrementCheckLabel
       *    lwsync <if !unconditionalSync>
       *    st     tempReg, lwOffset(baseReg)
       *    b      doneLabel
       *
       * decrementCheckLabel:
       *    li     tempReg, LOCK_NON_PRIMITIVE_EXIT_IGNORE_MASK
       *    andc   tempReg, monitorReg, tempReg
       *    cmpl   cr0, tempReg, metaReg
       *    bne    callLabel
       *    andi.  tempReg, monitorReg, OBJECT_HEADER_LOCK_RECURSION_MASK
       *    beq    callLabel
       *    addi   monitorReg, monitorReg, -LOCK_INC_DEC_VALUE
       *    st     monitorReg, lwOffset(baseReg)
       *
       * doneLabel:
       * #if JAVA_SPEC_VERSION >= 19
       *    ld/lwz tempReg, OwnedMonitorCountOffset(metaReg)
       *    addi   tempReg, tempReg, -1
       *    st     tempReg, OwnedMonitorCountOffset(metaReg)
       * #endif //JAVA_SPEC_VERSION >= 19
       * callReturnLabel:
       * === OUT OF LINE ===
       * callLabel:
       *    bl    jitMonitorExit
       *    b     callReturnLabel
       */

      TR::LabelSymbol *decrementCheckLabel, *doneLabel;

      decrementCheckLabel = generateLabelSymbol(cg);
      doneLabel = generateLabelSymbol(cg);

      generateTrg1MemInstruction(cg, loadOpCode, node, monitorReg, TR::MemoryReference::createWithDisplacement(cg, baseReg, lwOffset, lockSize));
      generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, tempReg, 0);
      generateTrg1Src2Instruction(cg, compareLogicalOpCode, node, condReg, monitorReg, metaReg);
      if (comp->target().isSMP() && unconditionalSync == true)
         generateInstruction(cg, TR::InstOpCode::lwsync, node);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, decrementCheckLabel, condReg);

      // exiting from read monitors still needs lwsync
      // (ensures loads have completed before releasing lock)
      if (comp->target().isSMP() && unconditionalSync == false)
         generateInstruction(cg, TR::InstOpCode::lwsync, node);

      generateMemSrc1Instruction(cg, storeOpCode, node, TR::MemoryReference::createWithDisplacement(cg, baseReg, lwOffset, lockSize), tempReg);
      generateLabelInstruction(cg, TR::InstOpCode::b, node, doneLabel);

      generateLabelInstruction(cg, TR::InstOpCode::label, node, decrementCheckLabel);
      generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, tempReg, LOCK_NON_PRIMITIVE_EXIT_IGNORE_MASK);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::andc, node, tempReg, monitorReg, tempReg);
      generateTrg1Src2Instruction(cg, compareLogicalOpCode, node, condReg, tempReg, metaReg);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, callLabel, condReg);

      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, tempReg, monitorReg, OBJECT_HEADER_LOCK_RECURSION_MASK);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, callLabel, condReg);

      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, monitorReg, monitorReg, -LOCK_INC_DEC_VALUE);
      generateMemSrc1Instruction(cg, storeOpCode, node, TR::MemoryReference::createWithDisplacement(cg, baseReg, lwOffset, lockSize), monitorReg);

      generateLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel);
#if JAVA_SPEC_VERSION >= 19
      /*
       * Adjust J9VMThread ownedMonitorCount for execution paths that do not
       * go out of line to release the lock. It is safe and efficient to do
       * this unconditionally. There is no need to check for underflow.
       */
      generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, tempReg, TR::MemoryReference::createWithDisplacement(cg, metaReg, fej9->thisThreadGetOwnedMonitorCountOffset(), TR::Compiler->om.sizeofReferenceAddress()));
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, tempReg, tempReg, -1);
      generateMemSrc1Instruction(cg, TR::InstOpCode::Op_st, node, TR::MemoryReference::createWithDisplacement(cg, metaReg, fej9->thisThreadGetOwnedMonitorCountOffset(), TR::Compiler->om.sizeofReferenceAddress()), tempReg);
#endif //JAVA_SPEC_VERSION >= 19

      TR::LabelSymbol *callReturnLabel = generateLabelSymbol(cg);

      TR_PPCOutOfLineCodeSection *outlinedHelperCall = new (cg->trHeapMemory()) TR_PPCOutOfLineCodeSection(node, TR::call, NULL, callLabel, callReturnLabel, cg);
      cg->getPPCOutOfLineCodeSectionList().push_front(outlinedHelperCall);

      generateDepLabelInstruction(cg, TR::InstOpCode::label, node, callReturnLabel, conditions);
      callReturnLabel->setEndInternalControlFlow();

      conditions->stopUsingDepRegs(cg, objReg);
      cg->decReferenceCount(objNode);
      }
   else
      {
      // read-only locks, ReaderReg = 0x0000 0000
      //    li     tempReg, offset
      //    lwsync
      // loopLabel:
      //    lwarx  monitorReg, [baseReg, tempReg]
      //    cmpi   condReg, monitorReg, 0x6 // count == 1 (flc bit is set)
      //    addi   threadReg, monitorReg, -4
      //    beq    decLabel
      //    stwcx  [baseReg, tempReg], threadReg
      //    bne    loopLabel
      //    b      doneLabel
      // decLabel: (a misleading name, really a RestoreAndCallLabel)
      //    andi_r threadReg, monitorReg, 0x3
      //    or     threadReg, threadReg, metaReg
      //    stwcx  [baseReg, tempReg], threadReg
      //    beq    callLabel
      //    b      loopLabel
      // doneLabel:
      // callReturnLabel:
      // === OUT OF LINE ===
      // callLabel:
      //    bl   jitMonitorExit
      //    b    callReturnLabel;

      TR::LabelSymbol *loopLabel, *decLabel, *doneLabel;
      loopLabel = generateLabelSymbol(cg);
      decLabel = generateLabelSymbol(cg);
      doneLabel = generateLabelSymbol(cg);

      generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, tempReg, lwOffset);

      // exiting from read monitors still needs lwsync
      // (ensures loads have completed before releasing lock)
      if (comp->target().isSMP())
         generateInstruction(cg, TR::InstOpCode::lwsync, node);

      generateDepLabelInstruction(cg, TR::InstOpCode::label, node, loopLabel, conditions);
      generateTrg1MemInstruction(cg, reservedLoadOpCode, node, monitorReg, TR::MemoryReference::createWithIndexReg(cg, baseReg, tempReg, lockSize));

      generateTrg1Src1ImmInstruction(cg, compareLogicalImmOpCode, node, condReg, monitorReg, 0x6);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, threadReg, monitorReg, -4);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, decLabel, condReg);

      generateMemSrc1Instruction(cg, conditionalStoreOpCode, node, TR::MemoryReference::createWithIndexReg(cg, baseReg, tempReg, lockSize), threadReg);

      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, PPCOpProp_BranchUnlikely, node, loopLabel, condReg);
      generateLabelInstruction(cg, TR::InstOpCode::b, node, doneLabel);

      generateLabelInstruction(cg, TR::InstOpCode::label, node, decLabel);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, threadReg, monitorReg, condReg, 0x3);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::OR, node, threadReg, threadReg, metaReg);
      generateMemSrc1Instruction(cg, conditionalStoreOpCode, node, TR::MemoryReference::createWithIndexReg(cg, baseReg, tempReg, lockSize), threadReg);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, callLabel, condReg);
      generateLabelInstruction(cg, TR::InstOpCode::b, node, loopLabel);

      generateDepLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel, conditions);

      doneLabel->setEndInternalControlFlow();

      TR::LabelSymbol *callReturnLabel = generateLabelSymbol(cg);

      TR_PPCOutOfLineCodeSection *outlinedHelperCall = new (cg->trHeapMemory()) TR_PPCOutOfLineCodeSection(node, TR::call, NULL, callLabel, callReturnLabel, cg);
      cg->getPPCOutOfLineCodeSectionList().push_front(outlinedHelperCall);

      generateLabelInstruction(cg, TR::InstOpCode::label, node, callReturnLabel);

      conditions->stopUsingDepRegs(cg, objReg);
      cg->decReferenceCount(objNode);
      }

   return (NULL);
   }

static void genInitObjectHeader(TR::Node *node, TR::Instruction *&iCursor, TR_OpaqueClassBlock *clazz, TR::Register *classReg, TR::Register *resReg, TR::Register *zeroReg,
      TR::Register *condReg, TR::Register *temp1Reg, TR::Register *temp2Reg, TR::Register * packedClobberedOffset, TR::RegisterDependencyConditions *conditions, bool needZeroInit,
      TR::CodeGenerator *cg);

static void genHeapAlloc(TR::Node *node, TR::Instruction *&iCursor, TR_OpaqueClassBlock *clazz, bool isVariableLen, TR::Register *enumReg, TR::Register *classReg, TR::Register *resReg, TR::Register *zeroReg, TR::Register *condReg,
      TR::Register *dataSizeReg, TR::Register *heapTopReg, TR::Register *sizeReg, TR::Register *temp1Reg, TR::Register *temp2Reg, TR::Register *temp3Reg, TR::LabelSymbol *callLabel, TR::LabelSymbol *doneLabel,
      int32_t allocSize, int32_t elementSize, bool usingTLH, bool needZeroInit,  TR::RegisterDependencyConditions *dependencies, TR::CodeGenerator *cg)
   {
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());
   TR::Register *metaReg = cg->getMethodMetaDataRegister();
   TR::ILOpCodes opCode = node->getOpCodeValue();

   if (comp->getOptions()->realTimeGC())
      {
#if defined(J9VM_GC_REALTIME)
      // Use temp3Reg to hold the javaVM.
      iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp3Reg,
                                           TR::MemoryReference::createWithDisplacement(cg, metaReg, fej9->thisThreadJavaVMOffset(), TR::Compiler->om.sizeofReferenceAddress()), iCursor);

      // Realtime size classes are now a pointer in J9JavaVM rather than an inlined struct of arrays, so we can't use J9JavaVM as a base pointer anymore
      // Use temp3Reg to hold J9JavaVM->realTimeSizeClasses
      iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp3Reg,
                                           TR::MemoryReference::createWithDisplacement(cg, temp3Reg, fej9->getRealtimeSizeClassesOffset(), TR::Compiler->om.sizeofReferenceAddress()), iCursor);

      // heap allocation, so proceed
      if (isVariableLen)
         {
         // make sure size isn't too big
         // convert max object size to num elements because computing an object size from num elements may overflow

         uintptr_t maxSize = fej9->getMaxObjectSizeForSizeClass();
         TR_ASSERT(((int32_t)(maxSize)-allocSize)/elementSize <= USHRT_MAX, "MaxObjectSizeForSizeClass won't fit into 16-bits.");

         iCursor = generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpli, node, condReg, enumReg, ((int32_t)(maxSize)-allocSize)/elementSize, iCursor);
         iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bgt, node, callLabel, condReg, iCursor);

         if (TR::Compiler->om.useHybridArraylets())
            {
            // Zero length arrays are discontiguous (i.e. they also need the discontiguous length field to be 0) because
            // they are indistinguishable from non-zero length discontiguous arrays
            iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpli4, node, condReg, enumReg, 0, iCursor);
            iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, callLabel, condReg, iCursor);
            }

         // now compute size of object in bytes
         TR_ASSERT(elementSize == 1 || elementSize == 2 || elementSize == 4 || elementSize == 8 || elementSize == 16, "Illegal element size for genHeapAlloc()");

         TR::Register *scaledSizeReg = enumReg;

         if (elementSize > 1)
            {
            iCursor = generateShiftLeftImmediate(cg, node, dataSizeReg, enumReg, trailingZeroes(elementSize), iCursor);
            scaledSizeReg = dataSizeReg;
            }

         // need to round up to sizeof(UDATA) so we can use it to index into size class index array
         // conservatively just add sizeof(UDATA) bytes and round
         if (elementSize < sizeof(UDATA))
            iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, dataSizeReg, scaledSizeReg, allocSize + sizeof(UDATA) - 1, iCursor);
         else
            iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, dataSizeReg, scaledSizeReg, allocSize, iCursor);

         if (elementSize < sizeof(UDATA))
            {
            if (comp->target().is64Bit())
               iCursor = generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldicr, node, dataSizeReg, dataSizeReg, 0, int64_t(-sizeof(UDATA)), iCursor);
            else
               iCursor = generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, dataSizeReg, dataSizeReg, 0, -sizeof(UDATA), iCursor);
            }

#ifdef J9VM_INTERP_FLAGS_IN_CLASS_SLOT
         iCursor = generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpli, node, condReg, dataSizeReg, J9_GC_MINIMUM_OBJECT_SIZE, iCursor);
         TR::LabelSymbol *doneLabel = generateLabelSymbol(cg);
         iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bge, PPCOpProp_BranchLikely, node, doneLabel, condReg, iCursor);
         iCursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, dataSizeReg, J9_GC_MINIMUM_OBJECT_SIZE, iCursor);
         iCursor = generateLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel, iCursor);
#endif

         // J9JavaVM (or realTimeSizeClasses when J9_CHANGES_PR95193 is defined) + rounded data size + SizeClassesIndexOffset is pointer to the size class
         iCursor = generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, temp1Reg, dataSizeReg, temp3Reg, iCursor);

         //Now adjust dataSizeReg to only include the size of the array data in bytes, since this will be used in genInitArrayHeader();
         iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, dataSizeReg, dataSizeReg, -allocSize, iCursor);
         iCursor = generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, heapTopReg,
                                              TR::MemoryReference::createWithDisplacement(cg, temp1Reg, fej9->getSizeClassesIndexOffset(), TR::Compiler->om.sizeofReferenceAddress()), iCursor);
         // heapTopReg now holds size class

         // get next cell for this size class at vmThread + thisThreadAllocationCacheCurrentOffset(0) + (size class)*sizeof(J9VMGCSegregatedAllocationCacheEntry)
         TR_ASSERT(sizeof(J9VMGCSegregatedAllocationCacheEntry) == sizeof(UDATA*) * 2,
                   "J9VMGCSegregatedAllocationCacheEntry may need to be padded to avoid multiply in array access.");

         if (comp->target().is64Bit())
            iCursor = generateShiftLeftImmediateLong(cg, node, temp1Reg, heapTopReg, trailingZeroes((int32_t)sizeof(J9VMGCSegregatedAllocationCacheEntry)), iCursor);
         else
            iCursor = generateShiftLeftImmediate(cg, node, temp1Reg, heapTopReg, trailingZeroes((int32_t)sizeof(J9VMGCSegregatedAllocationCacheEntry)), iCursor);

         iCursor = generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, sizeReg, temp1Reg, metaReg, iCursor);
         iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, resReg,
                                              TR::MemoryReference::createWithDisplacement(cg, sizeReg, fej9->thisThreadAllocationCacheCurrentOffset(0), TR::Compiler->om.sizeofReferenceAddress()), iCursor);
         iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp1Reg,
                                              TR::MemoryReference::createWithDisplacement(cg, sizeReg, fej9->thisThreadAllocationCacheTopOffset(0), TR::Compiler->om.sizeofReferenceAddress()), iCursor);

         // if we've reached the top, then no cell available, use slow path
         iCursor = generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, condReg, resReg, temp1Reg, iCursor);
         iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bge, node, callLabel, condReg, iCursor);

         // have a valid cell, need to update current cell pointer
         if (comp->target().is64Bit())
            iCursor = generateShiftLeftImmediateLong(cg, node, temp1Reg, heapTopReg, trailingZeroes((int32_t)sizeof(UDATA)), iCursor);
         else
            iCursor = generateShiftLeftImmediate(cg, node, temp1Reg, heapTopReg, trailingZeroes((int32_t)sizeof(UDATA)), iCursor);

         iCursor = generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, temp1Reg, temp1Reg, temp3Reg, iCursor);

         iCursor = generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, temp1Reg,
                                              TR::MemoryReference::createWithDisplacement(cg, temp1Reg, fej9->getSmallCellSizesOffset(), TR::Compiler->om.sizeofReferenceAddress()), iCursor);

         // temp1Reg now holds cell size
         iCursor = generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, temp1Reg, temp1Reg, resReg, iCursor);

         //temp1Reg now holds new current cell pointer. Update it in memory:
         iCursor = generateMemSrc1Instruction(cg,TR::InstOpCode::Op_st, node,
                                              TR::MemoryReference::createWithDisplacement(cg, sizeReg, fej9->thisThreadAllocationCacheCurrentOffset(0), TR::Compiler->om.sizeofReferenceAddress()), temp1Reg, iCursor);
         }
      else
         {
         // sizeClass will be bogus for variable length allocations because it only includes the header size (+ arraylet ptr for arrays)
         UDATA sizeClass = fej9->getObjectSizeClass(allocSize);

         iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, resReg, TR::MemoryReference::createWithDisplacement(cg, metaReg,
                     fej9->thisThreadAllocationCacheCurrentOffset(sizeClass), TR::Compiler->om.sizeofReferenceAddress()), iCursor);
         iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp2Reg, TR::MemoryReference::createWithDisplacement(cg, metaReg,
                     fej9->thisThreadAllocationCacheTopOffset(sizeClass), TR::Compiler->om.sizeofReferenceAddress()), iCursor);
         iCursor = generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, condReg, resReg, temp2Reg, iCursor);
         iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bge, node, callLabel, condReg, iCursor);

         //now bump the current updatepointer
         if (fej9->getCellSizeForSizeClass(sizeClass) <= UPPER_IMMED)
            iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, temp1Reg, resReg, fej9->getCellSizeForSizeClass(sizeClass), iCursor);
         else
            {
            iCursor = loadConstant(cg, node, (int32_t)fej9->getCellSizeForSizeClass(sizeClass), temp1Reg, iCursor);
            iCursor = generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, temp1Reg, temp1Reg, resReg, iCursor);
            }
         iCursor = generateMemSrc1Instruction(cg,TR::InstOpCode::Op_st, node,
                                              TR::MemoryReference::createWithDisplacement(cg, metaReg, fej9->thisThreadAllocationCacheCurrentOffset(sizeClass), TR::Compiler->om.sizeofReferenceAddress()), temp1Reg, iCursor);
         }

      iCursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, zeroReg, 0, iCursor);

      //we're done
      return;
#endif
      } // if (comp->getOptions()->realTimeGC())
   else
      {
      uint32_t maxSafeSize = cg->getMaxObjectSizeGuaranteedNotToOverflow();

      bool generateArraylets = comp->generateArraylets();
      bool isArrayAlloc = (opCode == TR::newarray || opCode == TR::anewarray);

      if (isArrayAlloc)
         {
         if (TR::Compiler->om.useHybridArraylets())
            {
            int32_t maxContiguousArraySize = TR::Compiler->om.maxContiguousArraySizeInBytes() / elementSize;
            TR_ASSERT(maxContiguousArraySize >= 0, "Unexpected negative size for max contiguous array size");
            if (maxContiguousArraySize > UPPER_IMMED)
               {
               iCursor = loadConstant(cg, node, maxContiguousArraySize, temp2Reg, iCursor);
               iCursor = generateTrg1Src2Instruction(cg, TR::InstOpCode::cmpl4, node, condReg, enumReg, temp2Reg, iCursor);
               }
            else
               iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpli4, node, condReg, enumReg, maxContiguousArraySize, iCursor);

            iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bgt, node, callLabel, condReg, iCursor);
            }

         if (isVariableLen)
            {  // Inline runtime zero length non-packed array allocation
               // Zero length arrays are discontiguous (i.e. they also need the discontiguous length field to be 0) because
               // they are indistinguishable from non-zero length discontiguous arrays
               iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpli4, node, condReg, enumReg, 0, iCursor);
               TR::LabelSymbol *nonZeroLengthLabel = generateLabelSymbol(cg);
               iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, nonZeroLengthLabel, condReg, iCursor);

               int32_t zeroLenArraySize = (TR::Compiler->om.discontiguousArrayHeaderSizeInBytes() + TR::Compiler->om.getObjectAlignmentInBytes() - 1) & (-TR::Compiler->om.getObjectAlignmentInBytes());
               TR_ASSERT(zeroLenArraySize >= J9_GC_MINIMUM_OBJECT_SIZE, "Zero-length array size must be bigger than MIN_OBJECT_SIZE");
               TR_ASSERT(zeroLenArraySize <= maxSafeSize, "Zero-length array size must be smaller than maxSafeSize");
               // Load TLH heapAlloc and heapTop values.
               iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, resReg,
                     TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, heapAlloc), TR::Compiler->om.sizeofReferenceAddress()), iCursor);
               iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, heapTopReg,
                     TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, heapTop), TR::Compiler->om.sizeofReferenceAddress()), iCursor);

               iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, temp2Reg, resReg, zeroLenArraySize, iCursor);
               iCursor = generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, condReg, temp2Reg, heapTopReg, iCursor);
               iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bgt, node, callLabel, condReg, iCursor);

               if (comp->compileRelocatableCode())
                  genInitObjectHeader(node, iCursor, clazz, classReg, resReg, zeroReg, condReg, heapTopReg, sizeReg, NULL, dependencies, needZeroInit, cg);
               else
                  genInitObjectHeader(node, iCursor, clazz, NULL, resReg, zeroReg, condReg, heapTopReg, sizeReg, NULL, dependencies, needZeroInit, cg);

               UDATA offsetSizeFields = fej9->getOffsetOfDiscontiguousArraySizeField();
               iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node,
                                         TR::MemoryReference::createWithDisplacement(cg, resReg, offsetSizeFields-4, 4), enumReg, iCursor);
               iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node,
                                         TR::MemoryReference::createWithDisplacement(cg, resReg, offsetSizeFields, 4), enumReg, iCursor);

               iCursor = generateMemSrc1Instruction(cg,TR::InstOpCode::Op_st, node,
                            TR::MemoryReference::createWithDisplacement(cg, metaReg,
                            offsetof(J9VMThread, heapAlloc), TR::Compiler->om.sizeofReferenceAddress()), temp2Reg, iCursor);
               iCursor = generateLabelInstruction(cg, TR::InstOpCode::b, node, doneLabel);
               iCursor = generateLabelInstruction(cg, TR::InstOpCode::label, node, nonZeroLengthLabel);
            }
         }

      if (usingTLH)
         {
         bool sizeInReg = (isVariableLen || (allocSize > UPPER_IMMED));
         int32_t instanceBoundaryForAlignment = 64;

         static bool verboseDualTLH = feGetEnv("TR_verboseDualTLH") != NULL;

         if (isVariableLen)
            {
            // Detect large or negative number of elements in case addr wrap-around

            if (maxSafeSize < 0x00100000)
               {
               // NOTE: TR::InstOpCode::bge and the special shifts are used to cover every
               //       possible corner cases, with 32byte maximum object header.
               iCursor = generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpli, node, condReg, enumReg, (maxSafeSize >> 6) << 2, iCursor);
               iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bge, node, callLabel, condReg, iCursor);
               }
            else
               {
               uintptr_t mask;
               if (comp->target().is64Bit())
                  {
                  mask = 0xFFFFFFFF << (27 - leadingZeroes(maxSafeSize));
                  iCursor = generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm_r, node, temp2Reg, enumReg, condReg, 0, mask, iCursor);
                  }
               else
                  {
                  mask = (0xFFFFFFFF << (11 - leadingZeroes(maxSafeSize))) & 0x0000FFFF;
                  iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andis_r, node, temp2Reg, enumReg, condReg, mask, iCursor);
                  }
               iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, callLabel, condReg, iCursor);
               }
            }

         //TODO: This code is never executed, check if this can be deleted now.
         if (!cg->isDualTLH())
            {
            iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, resReg,
                  TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, heapAlloc), TR::Compiler->om.sizeofReferenceAddress()), iCursor);

            iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, heapTopReg,
                  TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, heapTop), TR::Compiler->om.sizeofReferenceAddress()), iCursor);

            if (needZeroInit)
               iCursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, zeroReg, 0, iCursor);
            }
         else
            {
            //DualTLH support, use nonZeroTLH if optimizer says we can.

            if (node->canSkipZeroInitialization())
               {

               if (verboseDualTLH)
                  fprintf(stderr, "DELETEME genHeapAlloc useNonZeroTLH [%d] isVariableLen [%d] node: [%p]  %s [@%s]\n", 1, isVariableLen, node, comp->signature(),
                        comp->getHotnessName(comp->getMethodHotness()));

               //Load non-zeroed TLH heapAlloc and heapTop values.
               iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, resReg,
                     TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, nonZeroHeapAlloc), TR::Compiler->om.sizeofReferenceAddress()), iCursor);

               iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, heapTopReg,
                     TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, nonZeroHeapTop), TR::Compiler->om.sizeofReferenceAddress()), iCursor);

               }
            else
               {
               if (verboseDualTLH)
                  fprintf(stderr, "DELETEME genHeapAlloc useNonZeroTLH [%d] isVariableLen [%d] node: [%p]  %s [@%s]\n", 0, isVariableLen, node, comp->signature(),
                        comp->getHotnessName(comp->getMethodHotness()));

               //Load zeroed TLH heapAlloc and heapTop values.
               iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, resReg,
                     TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, heapAlloc), TR::Compiler->om.sizeofReferenceAddress()), iCursor);

               iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, heapTopReg,
                     TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, heapTop), TR::Compiler->om.sizeofReferenceAddress()), iCursor);

               if (needZeroInit)
                   iCursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, zeroReg, 0, iCursor);

               }

            } //if(!cg->isDualTLH()) == false

         if (sizeInReg)
            {
            //Size will put into sizeReg
            //Get the size of the object
            //See if we can put into a single arraylet, if not call the helper.
            //Add enough padding to make it a multiple of OBJECT_ALIGNMENT
            if (isVariableLen)
               {
               int32_t elementSize;
               if (comp->useCompressedPointers() && node->getOpCodeValue() == TR::anewarray)
                  elementSize = TR::Compiler->om.sizeofReferenceField();
               else
                  elementSize = TR::Compiler->om.getSizeOfArrayElement(node);

               // Check to see if the size of the array will fit in a single arraylet leaf.
               //
               if (generateArraylets && (node->getOpCodeValue() == TR::anewarray || node->getOpCodeValue() == TR::newarray))
                  {
                  int32_t arrayletLeafSize = TR::Compiler->om.arrayletLeafSize();
                  int32_t maxContiguousArrayletLeafSizeInBytes = arrayletLeafSize - TR::Compiler->om.sizeofReferenceAddress(); //need to add definition
                  int32_t maxArrayletSizeInElements = maxContiguousArrayletLeafSizeInBytes / elementSize;
                  if (maxArrayletSizeInElements <= UPPER_IMMED)
                     {
                     iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, condReg, enumReg, maxArrayletSizeInElements, iCursor);
                     }
                  else
                     {
                     iCursor = loadConstant(cg, node, maxArrayletSizeInElements, temp1Reg, iCursor);
                     iCursor = generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp4, node, condReg, enumReg, temp1Reg, iCursor);
                     }
                  static const char *p = feGetEnv("TR_TarokPreLeafSizeCheckVarBreak");
                  if (p)
                     generateInstruction(cg, TR::InstOpCode::bad, node);

                  iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bge, node, callLabel, condReg, iCursor);
                  }

               int32_t round; // zero indicates no rounding is necessary
               round = (elementSize >= TR::Compiler->om.getObjectAlignmentInBytes()) ? 0 : TR::Compiler->om.getObjectAlignmentInBytes();
               bool headerAligned = allocSize % TR::Compiler->om.getObjectAlignmentInBytes() ? 0 : 1;

               //TODO: The code below pads up the object allocation size so that zero init code later
               //will have multiples of wordsize to work with. For now leaving this code as is, but
               //check if its worthwhile to remove these extra instructions added here for padding as
               //zero init will be removed now.
               if (elementSize >= 2)
                  {
                  if (comp->target().is64Bit())
                     iCursor = generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldic, node, dataSizeReg, enumReg, trailingZeroes(elementSize), CONSTANT64(0x00000000FFFFFFFF) << trailingZeroes(elementSize));
                  else
                     iCursor = generateShiftLeftImmediate(cg, node, dataSizeReg, enumReg, trailingZeroes(elementSize), iCursor);

                  }
               if (needZeroInit && elementSize <= 2)
                  {
                  // the zero initialization code uses a loop of stwu's, and
                  // so dataSizeReg must be rounded up to a multiple of 4
                  if (elementSize == 2)
                     iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, dataSizeReg, dataSizeReg, 3, iCursor);
                  else  //elementSize == 1
                     iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, dataSizeReg, enumReg, 3, iCursor);

                  if (comp->target().is64Bit() && elementSize != 1)
                     iCursor = generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldicr, node, dataSizeReg, dataSizeReg, 0, int64_t(-4), iCursor);
                  else
                     iCursor = generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, dataSizeReg, dataSizeReg, 0, -4, iCursor);
                  }

               if ((round != 0 && (!needZeroInit || round != 4)) || !headerAligned)
                  {
                  // Round the total size to a multiple of OBJECT_ALIGNMENT
                  if (elementSize == 1 && !needZeroInit)
                     iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, sizeReg, enumReg, allocSize + round - 1, iCursor);
                  else
                     iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, sizeReg, dataSizeReg, allocSize + round - 1, iCursor);
                  if (comp->target().is64Bit() && elementSize != 1)
                     iCursor = generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldicr, node, sizeReg, sizeReg, 0, int64_t(-round), iCursor);
                  else
                     iCursor = generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, sizeReg, sizeReg, 0, -round, iCursor);
                  }
               else
                  {
                  if (elementSize == 1 && !needZeroInit)
                     iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, sizeReg, enumReg, allocSize, iCursor);
                  else
                     iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, sizeReg, dataSizeReg, allocSize, iCursor);
                  }

#ifdef J9VM_INTERP_FLAGS_IN_CLASS_SLOT
               if (!((node->getOpCodeValue() == TR::New) || (node->getOpCodeValue() == TR::anewarray) || (node->getOpCodeValue() == TR::newarray)))
                  {
                  iCursor = generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpli, node, condReg, sizeReg, J9_GC_MINIMUM_OBJECT_SIZE, iCursor);
                  TR::LabelSymbol *doneLabel = generateLabelSymbol(cg);
                  iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bge, PPCOpProp_BranchLikely, node, doneLabel, condReg, iCursor);

                  iCursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, sizeReg, J9_GC_MINIMUM_OBJECT_SIZE, iCursor);
                  iCursor = generateLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel, iCursor);
                  }
#endif
               } //if (isVariableLen)
            else
               {
               // Check to see if the size of the array will fit in a single arraylet leaf.
               //
               if (generateArraylets && (node->getOpCodeValue() == TR::anewarray || node->getOpCodeValue() == TR::newarray))
                  {
                  int32_t arrayletLeafSize = TR::Compiler->om.arrayletLeafSize();

                  if (allocSize >= arrayletLeafSize + TR::Compiler->om.contiguousArrayHeaderSizeInBytes())
                     {
                     static const char *p = feGetEnv("TR_TarokPreLeafSizeCheckConstBreak");
                     if (p)
                        generateInstruction(cg, TR::InstOpCode::bad, node);

                     iCursor = generateLabelInstruction(cg, TR::InstOpCode::b, node, callLabel, iCursor);
                     }
                  }

               iCursor = loadConstant(cg, node, allocSize, sizeReg, iCursor);
               }
            } //if (sizeInReg)

         // Calculate the after-allocation heapAlloc: if the size is huge,
         //   we need to check address wrap-around also. This is unsigned
         //   integer arithmetic, checking carry bit is enough to detect it.
         //   For variable length array, we did an up-front check already.
         if (sizeInReg)
            iCursor = generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, temp2Reg, resReg, sizeReg, iCursor);
         else
            iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, temp2Reg, resReg, allocSize, iCursor);

         if (!isVariableLen && ((uint32_t) allocSize) > maxSafeSize)
            {
            iCursor = generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, condReg, temp2Reg, resReg, iCursor);
            iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, callLabel, condReg, iCursor);
            }

         // We need to make sure that the TLH pointer is bumped correctly to support the Arraylet header.
         //
         if (generateArraylets && (node->getOpCodeValue() == TR::anewarray || node->getOpCodeValue() == TR::newarray))
            {
            iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, temp2Reg, temp2Reg, TR::Compiler->om.getObjectAlignmentInBytes() - 1, iCursor);
            iCursor = generateTrg1Src1Imm2Instruction(cg, comp->target().is64Bit() ? TR::InstOpCode::rldicr : TR::InstOpCode::rlwinm, node, temp2Reg, temp2Reg, 0, int64_t(-TR::Compiler->om.getObjectAlignmentInBytes()), iCursor);
            static const char *p = feGetEnv("TR_TarokAlignHeapTopBreak");
            if (p)
               iCursor = generateInstruction(cg, TR::InstOpCode::bad, node, iCursor);
            }

         // Ok, temp2Reg now points to where the object will end on the TLH.
         //    resReg will contain the start of the object where we'll write out our
         //    J9Class*. Should look like this in memory:
         //    [heapAlloc == resReg] ... temp2Reg ...//... heapTopReg.

         //Here we check if we overflow the TLH Heap Top (heapTop, or nonZeroHeapTop)
         //branch to regular heapAlloc Snippet if we overflow (ie callLabel).
         iCursor = generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, condReg, temp2Reg, heapTopReg, iCursor);
         iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bgt, node, callLabel, condReg, iCursor);

         if (cg->enableTLHPrefetching())
            {
            //Decide between zeroed and non-zero TLH'es

            if (cg->isDualTLH() && node->canSkipZeroInitialization())
               {

               if (verboseDualTLH)
                  fprintf(stderr, "DELETEME genHeapAlloc PREFETCH useNonZeroTLH [%d] isVariableLen [%d] node: [%p]  %s [@%s]\n", 1, isVariableLen, node, comp->signature(),
                        comp->getHotnessName(comp->getMethodHotness()));

               iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp1Reg,
                     TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, nonZeroTlhPrefetchFTA), TR::Compiler->om.sizeofReferenceAddress()), iCursor);
               }
            else
               {
               if (verboseDualTLH)
                  fprintf(stderr, "DELETEME genHeapAlloc PREFETCH useNonZeroTLH [%d] isVariableLen [%d] node: [%p]  %s [@%s]\n", 0, isVariableLen, node, comp->signature(),
                        comp->getHotnessName(comp->getMethodHotness()));

               iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp1Reg,
                     TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, tlhPrefetchFTA), TR::Compiler->om.sizeofReferenceAddress()), iCursor);
               }

            if (sizeInReg)
               iCursor = generateTrg1Src2Instruction(cg, TR::InstOpCode::subf_r, node, temp1Reg, sizeReg, temp1Reg, condReg, iCursor);
            else
               iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addic_r, node, temp1Reg, temp1Reg, condReg, -allocSize, iCursor);

            //Write back and allocate Snippet if needed.
            TR::LabelSymbol * callLabel = NULL;
            if (cg->isDualTLH() && node->canSkipZeroInitialization())
               {
               iCursor = generateMemSrc1Instruction(cg,TR::InstOpCode::Op_st, node,
                     TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, nonZeroTlhPrefetchFTA), TR::Compiler->om.sizeofReferenceAddress()), temp1Reg, iCursor);

               callLabel = cg->lookUpSnippet(TR::Snippet::IsNonZeroAllocPrefetch, NULL);
               if (callLabel == NULL)
                  {
                  callLabel = generateLabelSymbol(cg);
                  TR::Snippet *snippet = new (cg->trHeapMemory()) TR::PPCNonZeroAllocPrefetchSnippet(cg, node, callLabel);
                  cg->addSnippet(snippet);
                  }
               }
            else
               {
               iCursor = generateMemSrc1Instruction(cg,TR::InstOpCode::Op_st, node,
                     TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, tlhPrefetchFTA), TR::Compiler->om.sizeofReferenceAddress()), temp1Reg, iCursor);

               callLabel = cg->lookUpSnippet(TR::Snippet::IsAllocPrefetch, NULL);
               if (callLabel == NULL)
                  {
                  callLabel = generateLabelSymbol(cg);
                  TR::Snippet *snippet = new (cg->trHeapMemory()) TR::PPCAllocPrefetchSnippet(cg, node, callLabel);
                  cg->addSnippet(snippet);
                  }
               }

            // kills temp1Reg and sizeReg
            iCursor = generateDepConditionalBranchInstruction(cg, TR::InstOpCode::blel, PPCOpProp_BranchUnlikely, node, callLabel, condReg, dependencies, iCursor);

            cg->machine()->setLinkRegisterKilled(true);
            }

         //Done, write back to heapAlloc (zero or nonZero TLH) here.
         if (cg->isDualTLH() && node->canSkipZeroInitialization())
            {
            iCursor = generateMemSrc1Instruction(cg,TR::InstOpCode::Op_st, node,
                  TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, nonZeroHeapAlloc), TR::Compiler->om.sizeofReferenceAddress()), temp2Reg, iCursor);
            }
         else
            {
            iCursor = generateMemSrc1Instruction(cg,TR::InstOpCode::Op_st, node,
                  TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, heapAlloc), TR::Compiler->om.sizeofReferenceAddress()), temp2Reg, iCursor);
            }
         }
      else
         {
         bool sizeInReg = (allocSize > UPPER_IMMED);
         TR::LabelSymbol *tryLabel = generateLabelSymbol(cg);

         iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, heapTopReg,
               TR::MemoryReference::createWithDisplacement(cg, temp1Reg, offsetof(J9MemorySegment, heapTop), TR::Compiler->om.sizeofReferenceAddress()), iCursor);
         iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, dataSizeReg, temp1Reg, offsetof(J9MemorySegment, heapAlloc), iCursor);
         iCursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, zeroReg, 0, iCursor);
         if (sizeInReg)
            {
            if (0x00008000 == HI_VALUE(allocSize))
               {
               iCursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::lis, node, sizeReg, 0x7FFF, iCursor);
               iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addis, node, sizeReg, sizeReg, 0x1, iCursor);
               }
            else
               {
               iCursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::lis, node, sizeReg, HI_VALUE(allocSize), iCursor);
               }
            iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, sizeReg, sizeReg, LO_VALUE(allocSize), iCursor);
            }

         // Try to allocate
         iCursor = generateLabelInstruction(cg, TR::InstOpCode::label, node, tryLabel, iCursor);
         iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, resReg, TR::MemoryReference::createWithDisplacement(cg, dataSizeReg, 0, TR::Compiler->om.sizeofReferenceAddress()), iCursor);

         // Calculate the after-allocation heapAlloc: if the size is huge,
         //   we need to check address wrap-around also. This is unsigned
         //   integer arithmetic, checking carry bit is enough to detect it.
         if ((uint32_t) allocSize > maxSafeSize)
            {
            if (sizeInReg)
               {
               iCursor = generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, temp1Reg, resReg, sizeReg, iCursor);
               }
            else
               {
               iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, temp1Reg, resReg, allocSize, iCursor);
               }

            iCursor = generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, condReg, temp1Reg, resReg, iCursor);

            iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, callLabel, condReg, iCursor);
            }
         else
            {
            if (sizeInReg)
               {
               iCursor = generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, temp1Reg, resReg, sizeReg, iCursor);
               }
            else
               {
               iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, temp1Reg, resReg, allocSize, iCursor);
               }
            }

         iCursor = generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, condReg, temp1Reg, heapTopReg, iCursor);
         iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bgt, node, callLabel, condReg, iCursor);

         // todo64: I think these need to be ldarx/stdcx. in 64-bit mode
         iCursor = generateTrg1MemInstruction(cg, TR::InstOpCode::lwarx, node, temp2Reg, TR::MemoryReference::createWithIndexReg(cg, dataSizeReg, zeroReg, 4), iCursor);
         iCursor = generateTrg1Src2Instruction(cg, TR::InstOpCode::cmpl4, node, condReg, temp2Reg, resReg, iCursor);
         iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, tryLabel, condReg, iCursor);
         iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stwcx_r, node, TR::MemoryReference::createWithIndexReg(cg, dataSizeReg, zeroReg, 4), temp1Reg, iCursor);

         iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, PPCOpProp_BranchUnlikely, node, tryLabel, condReg, iCursor);
         }
      }
   }


static void genInitObjectHeader(TR::Node *node, TR::Instruction *&iCursor, TR_OpaqueClassBlock *clazz, TR::Register *classReg, TR::Register *resReg, TR::Register *zeroReg,
      TR::Register *condReg, TR::Register *temp1Reg, TR::Register *temp2Reg, TR::Register * packedClobberedOffset, TR::RegisterDependencyConditions *conditions, bool needZeroInit,
      TR::CodeGenerator *cg)
   {
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());
   J9ROMClass *romClass = 0;
   uint32_t staticFlag = 0;
   uint32_t orFlag = 0;
   TR::Compilation *comp = cg->comp();

   TR_ASSERT(clazz, "Cannot have a null OpaqueClassBlock\n");
   romClass = TR::Compiler->cls.romClassOf(clazz);
   staticFlag = romClass->instanceShape;
   TR::Register *metaReg = cg->getMethodMetaDataRegister();

   TR::Register * clzReg = classReg;

   if (comp->compileRelocatableCode() && !comp->getOption(TR_UseSymbolValidationManager))
      {
      if (node->getOpCodeValue() == TR::newarray)
         {
         iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp1Reg,
               TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, javaVM), TR::Compiler->om.sizeofReferenceAddress()), iCursor);
         iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp1Reg,
               TR::MemoryReference::createWithDisplacement(cg, temp1Reg, ((TR_J9VM *) fej9)->getPrimitiveArrayOffsetInJavaVM(node->getSecondChild()->getInt()),
                     TR::Compiler->om.sizeofReferenceAddress()), iCursor);
         clzReg = temp1Reg;
         }
      else if (node->getOpCodeValue() == TR::anewarray)
         {
         TR_ASSERT(classReg, "must have a classReg for TR::anewarray in AOT mode");
         iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp1Reg,
               TR::MemoryReference::createWithDisplacement(cg, classReg, offsetof(J9Class, arrayClass), TR::Compiler->om.sizeofReferenceAddress()), iCursor);
         clzReg = temp1Reg;
         }
      else
         {
         TR_ASSERT(node->getOpCodeValue() == TR::New && classReg, "must have a classReg for TR::New in AOT mode");
         clzReg = classReg;
         }
      }

   // Store the class
   if (clzReg == NULL)
      {
#ifdef TR_TARGET_64BIT
      int32_t offset;
      intptr_t classPtr = (intptr_t)clazz;

      offset = TR_PPCTableOfConstants::lookUp((int8_t *)&classPtr, sizeof(intptr_t), true, 0, cg);

      if (offset != PTOC_FULL_INDEX)
         {
         offset *= TR::Compiler->om.sizeofReferenceAddress();
         if (TR_PPCTableOfConstants::getTOCSlot(offset) == 0)
         TR_PPCTableOfConstants::setTOCSlot(offset, (int64_t)clazz);
         if (offset<LOWER_IMMED||offset>UPPER_IMMED)
            {
            TR_ASSERT_FATAL_WITH_NODE(node, 0x00008000 != HI_VALUE(offset), "TOC offset (0x%x) is unexpectedly high. Can not encode upper 16 bits into an addis instruction.", offset);
            iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addis, node, temp1Reg, cg->getTOCBaseRegister(), HI_VALUE(offset), iCursor);
            iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp1Reg, TR::MemoryReference::createWithDisplacement(cg, temp1Reg, LO_VALUE(offset), TR::Compiler->om.sizeofReferenceAddress()), iCursor);
            }
         else
            {
            iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp1Reg, TR::MemoryReference::createWithDisplacement(cg, cg->getTOCBaseRegister(), offset, TR::Compiler->om.sizeofReferenceAddress()), iCursor);
            }
         if (orFlag != 0)
            {
            iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::ori, node, temp1Reg, temp1Reg, orFlag, iCursor);
            }
         }
      else
         {
         iCursor = loadConstant(cg, node, (int64_t)clazz|(int64_t)orFlag, temp1Reg, iCursor);
         }
#else
      iCursor = loadConstant(cg, node, (int32_t) clazz | (int32_t) orFlag, temp1Reg, iCursor);
#endif /* TR_TARGET_64BIT */

      if (TR::Compiler->om.compressObjectReferences())
         // must store only 32 bits
         iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node,
              TR::MemoryReference::createWithDisplacement(cg, resReg, (int32_t)TR::Compiler->om.offsetOfObjectVftField(), 4),
               temp1Reg, iCursor);
      else
         iCursor = generateMemSrc1Instruction(cg,TR::InstOpCode::Op_st, node,
               TR::MemoryReference::createWithDisplacement(cg, resReg, (int32_t) TR::Compiler->om.offsetOfObjectVftField(), TR::Compiler->om.sizeofReferenceAddress()), temp1Reg, iCursor);
      }
   else
      {
      if (orFlag != 0)
         {
         iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::ori, node, clzReg, clzReg, orFlag, iCursor);
         }
      if (TR::Compiler->om.compressObjectReferences())
         // must store only 32 bits
         iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node,
               TR::MemoryReference::createWithDisplacement(cg, resReg, (int32_t)TR::Compiler->om.offsetOfObjectVftField(), 4),
               clzReg, iCursor);
      else
         iCursor = generateMemSrc1Instruction(cg,TR::InstOpCode::Op_st, node,
               TR::MemoryReference::createWithDisplacement(cg, resReg, (int32_t) TR::Compiler->om.offsetOfObjectVftField(), TR::Compiler->om.sizeofReferenceAddress()), clzReg, iCursor);
      }


#ifndef J9VM_INTERP_FLAGS_IN_CLASS_SLOT

   bool isStaticFlag = fej9->isStaticObjectFlags();
   if (isStaticFlag)
      {
      // The object flags can be determined at compile time.
      staticFlag |= fej9->getStaticObjectFlags();
      if (staticFlag != 0 || needZeroInit)
         {
         iCursor = loadConstant(cg, node, (int32_t) staticFlag, temp2Reg, iCursor);
         // Store the flags
         iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, new (cg->trHeapMemory()) TR::MemoryReference(resReg, (int32_t) TMP_OFFSETOF_J9OBJECT_FLAGS, 4, cg), temp2Reg,
               iCursor);
         }
      }
   else if (!comp->getOptions()->realTimeGC())
      {
      // If the object flags cannot be determined at compile time, we add a load for it.
      //
      iCursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp2Reg,
            TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, allocateThreadLocalHeap.objectFlags), TR::Compiler->om.sizeofReferenceAddress()), iCursor);

      // OR staticFlag with temp2Reg.
      // For now, only the lower 16 bits are set in staticFlag. But check the higher 16 bits just in case.
      if (staticFlag != 0)
         {
         if ((staticFlag & 0xFFFF0000) != 0)
            {
            iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::oris, node, temp2Reg, temp2Reg, (staticFlag >> 16) & 0x0000FFFF, iCursor);
            }
         if ((staticFlag & 0x0000FFFF) != 0)
            {
            iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::ori, node, temp2Reg, temp2Reg, staticFlag & 0x0000FFFF, iCursor);
            }
         }
      iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, resReg, (int32_t) TMP_OFFSETOF_J9OBJECT_FLAGS, 4), temp2Reg,
            iCursor);
      }

#endif /*J9VM_INTERP_FLAGS_IN_CLASS_SLOT*/

   TR::InstOpCode::Mnemonic storeOpCode;
   int32_t lockSize;

   if (comp->target().is64Bit() && !fej9->generateCompressedLockWord())
      {
      storeOpCode = TR::InstOpCode::std;
      lockSize = 8;
      }
   else
      {
      storeOpCode = TR::InstOpCode::stw;
      lockSize = 4;
      }

   int32_t lwOffset = fej9->getByteOffsetToLockword(clazz);
   if (clazz && (lwOffset > 0))
      {
      int32_t lwInitialValue = fej9->getInitialLockword(clazz);

      if (0 != lwInitialValue)
         {
         iCursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, temp1Reg, lwInitialValue, iCursor);
         iCursor = generateMemSrc1Instruction(cg, storeOpCode, node, TR::MemoryReference::createWithDisplacement(cg, resReg, lwOffset, lockSize), temp1Reg, iCursor);
         }
      }
   }

static void genAlignArray(TR::Node *node, TR::Instruction *&iCursor, bool isVariableLen, TR::Register *resReg, int32_t objectSize, int32_t dataBegin, TR::Register *dataSizeReg,
      TR::Register *condReg, TR::Register *temp1Reg, TR::Register *temp2Reg, TR::CodeGenerator *cg)
   {
   TR::LabelSymbol *slotAtStart = generateLabelSymbol(cg);
   TR::LabelSymbol *doneAlign = generateLabelSymbol(cg);

   iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, temp1Reg, resReg, condReg, 7, iCursor);
   iCursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, temp2Reg, 3, iCursor);
   iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, slotAtStart, condReg, iCursor);

   // The slop bytes are at the end of the allocated object.
   if (isVariableLen)
      {
      iCursor = generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, temp1Reg, resReg, dataSizeReg, iCursor);
      iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, temp1Reg, dataBegin, 4), temp2Reg, iCursor);
      }
   else if (objectSize > UPPER_IMMED)
      {
      iCursor = loadConstant(cg, node, objectSize, temp1Reg, iCursor);
      iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithIndexReg(cg, resReg, temp1Reg, 4), temp2Reg, iCursor);
      }
   else
      {
      iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, resReg, objectSize, 4), temp2Reg, iCursor);
      }
   iCursor = generateLabelInstruction(cg, TR::InstOpCode::b, node, doneAlign, iCursor);

   // the slop bytes are at the start of the allocation
   iCursor = generateLabelInstruction(cg, TR::InstOpCode::label, node, slotAtStart, iCursor);
   iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, resReg, (int32_t) 0, 4), temp2Reg, iCursor);
   iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, resReg, resReg, 4, iCursor);
   iCursor = generateLabelInstruction(cg, TR::InstOpCode::label, node, doneAlign, iCursor);
   }

static void genInitArrayHeader(TR::Node *node, TR::Instruction *&iCursor, bool isVariableLen, TR_OpaqueClassBlock *clazz, TR::Register *classReg, TR::Register *resReg,
      TR::Register *zeroReg, TR::Register *condReg, TR::Register *eNumReg, TR::Register *dataSizeReg, TR::Register *temp1Reg, TR::Register *temp2Reg,
      TR::RegisterDependencyConditions *conditions, bool needZeroInit, TR::CodeGenerator *cg)
   {
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());
   TR::Register *instanceSizeReg;

   genInitObjectHeader(node, iCursor, clazz, classReg, resReg, zeroReg, condReg, temp1Reg, temp2Reg, NULL, conditions, needZeroInit, cg);

   instanceSizeReg = eNumReg;

   if ((node->getOpCodeValue() == TR::newarray || node->getOpCodeValue() == TR::anewarray) && node->getFirstChild()->getOpCode().isLoadConst() && (node->getFirstChild()->getInt() == 0))
      {// constant zero length non-packed array
       // Zero length arrays are discontiguous (i.e. they also need the discontiguous length field to be 0) because
       // they are indistinguishable from non-zero length discontiguous arrays
      iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node,
                                            TR::MemoryReference::createWithDisplacement(cg, resReg, fej9->getOffsetOfDiscontiguousArraySizeField()-4, 4),
                                            instanceSizeReg, iCursor);
      iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node,
                                            TR::MemoryReference::createWithDisplacement(cg, resReg, fej9->getOffsetOfDiscontiguousArraySizeField(), 4),
                                            instanceSizeReg, iCursor);
      }
   else
      {
      // Store the array size
      iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node,
                                           TR::MemoryReference::createWithDisplacement(cg, resReg, fej9->getOffsetOfContiguousArraySizeField(), 4),
                                           instanceSizeReg, iCursor);
      }
   }

static void genZeroInit(TR::CodeGenerator *cg, TR::Node *node, TR::Register *objectReg, int32_t headerSize, int32_t totalSize, bool useInitInfo)
   {
   TR_ASSERT((totalSize - headerSize > 0) && ((totalSize - headerSize) % 4 == 0), "Expecting non-zero word-aligned data size");

   TR::Register *zeroReg = cg->allocateRegister();
   TR::Compilation* comp = cg->comp();

   generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, zeroReg, 0);

   // Perform initialization if it is needed:
   //   1) Initialize certain array elements individually. This depends on the optimizer
   //      providing a "short" list of individual indices;
   //   2) Initialize the whole array:
   //      a) If the object size is constant and small use straight line code;
   //      b) For large objects and variable length arrays, do nothing, it was handled in the allocation helper.
   if (useInitInfo)
      {
      TR_ExtraInfoForNew *initInfo = node->getSymbolReference()->getExtraInfo();

      TR_ASSERT(initInfo && initInfo->zeroInitSlots && initInfo->numZeroInitSlots > 0, "Expecting valid init info");

      TR_BitVectorIterator bvi(*initInfo->zeroInitSlots);
      if (comp->target().is64Bit())
         {
         int32_t lastNotInit = -1;
         int32_t currElem = -1;

         // Try to group set of words into smallest groupings using double-word stores (where possible)
         while (bvi.hasMoreElements())
            {
            currElem = bvi.getNextElement();

            if (lastNotInit == -1)
               {
               // currently looking at only one slot (either just starting, or just emitted double-word store in last iter)
               lastNotInit = currElem;
               }
            else if (currElem == lastNotInit + 1)
               {
               // consecutive slots, can use std
               generateMemSrc1Instruction(cg, TR::InstOpCode::std, node, TR::MemoryReference::createWithDisplacement(cg, objectReg, headerSize + lastNotInit * 4, 8), zeroReg);
               lastNotInit = -1;
               }
            else
               {
               // Two non-consecutive slots, use stw for earlier slot and keep looking for slot
               // consecutive with the second slot currently held
               generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, objectReg, headerSize + lastNotInit * 4, 4), zeroReg);
               lastNotInit = currElem;
               }
            }

         if (lastNotInit != -1)
            {
            // Last slot is not consecutive with other slots, hasn't been initialized yet
            generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, objectReg, headerSize + lastNotInit * 4, 4), zeroReg);
            }
         }
      else
         {
         while (bvi.hasMoreElements())
            {
            generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node,
                  TR::MemoryReference::createWithDisplacement(cg, objectReg, headerSize + bvi.getNextElement() * TR::Compiler->om.sizeofReferenceAddress(), TR::Compiler->om.sizeofReferenceAddress()), zeroReg);
            }
         }
      }
   else
      {
      int32_t ofs;
      for (ofs = headerSize; ofs < totalSize - TR::Compiler->om.sizeofReferenceAddress(); ofs += TR::Compiler->om.sizeofReferenceAddress())
         {
         generateMemSrc1Instruction(cg,TR::InstOpCode::Op_st, node, TR::MemoryReference::createWithDisplacement(cg, objectReg, ofs, TR::Compiler->om.sizeofReferenceAddress()), zeroReg);
         }
      if (ofs + 4 == totalSize)
         generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, objectReg, ofs, 4), zeroReg);
      else if (ofs + 8 == totalSize)
         generateMemSrc1Instruction(cg, TR::InstOpCode::std, node, TR::MemoryReference::createWithDisplacement(cg, objectReg, ofs, 8), zeroReg);
      }

   // Scheduling barrier
   generateLabelInstruction(cg, TR::InstOpCode::label, node, generateLabelSymbol(cg));

   cg->stopUsingRegister(zeroReg);
   }

TR::Register *J9::Power::TreeEvaluator::VMnewEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR_J9VMBase *fej9 = cg->fej9();
   int32_t allocateSize, objectSize, dataBegin, idx;
   TR::ILOpCodes opCode;
   TR_OpaqueClassBlock *clazz;
   TR::Register *classReg, *resReg, *zeroReg = NULL;
   TR::RealRegister::RegNum resultRealReg = TR::RealRegister::gr8;
   TR::Register *condReg, *callResult, *enumReg, *dataSizeReg;
   TR::Register *tmp3Reg = NULL, *tmp4Reg, *tmp5Reg, *tmp6Reg, *tmp7Reg = NULL;
   TR::Register *packedRegAddr, *packedRegOffs, *packedRegLength;
   TR::LabelSymbol *callLabel, *callReturnLabel, *doneLabel;
   TR::RegisterDependencyConditions *conditions;
   TR::Instruction *iCursor = NULL;
   bool doInline = true, isArray = false, doublewordAlign = false;
   bool isVariableLen;
   bool insertType = false;
   static bool TR_noThreadLocalHeap = feGetEnv("TR_noThreadLocalHeap") ? 1 : 0;
   TR::Compilation * comp = cg->comp();
   bool generateArraylets = comp->generateArraylets();

   if (comp->getOption(TR_DisableTarokInlineArrayletAllocation) && (node->getOpCodeValue() == TR::anewarray || node->getOpCodeValue() == TR::newarray))
      doInline = false;

   bool usingTLH = !TR_noThreadLocalHeap;
   bool needZeroInit = (!usingTLH || !fej9->tlhHasBeenCleared()) || comp->getOptions()->realTimeGC();
   bool isDualTLH = cg->isDualTLH();

   int32_t elementSize = 0;

   opCode = node->getOpCodeValue();
   objectSize = comp->canAllocateInline(node, clazz);

   bool isArrayAlloc = (opCode == TR::newarray || opCode == TR::anewarray);
   bool isConstantLenArrayAlloc = isArrayAlloc && node->getFirstChild()->getOpCode().isLoadConst();
   bool isConstantZeroLenArrayAlloc = isConstantLenArrayAlloc && (node->getFirstChild()->getInt() == 0);

   if (isConstantZeroLenArrayAlloc)
      needZeroInit = false;

   TR_ASSERT(objectSize <= 0 || (objectSize & (TR::Compiler->om.sizeofReferenceAddress() - 1)) == 0, "object size causes an alignment problem");
   if (objectSize < 0 || (objectSize == 0 && !usingTLH))
      doInline = false;
   isVariableLen = (objectSize == 0);

   allocateSize = objectSize;

   if (opCode == TR::New)
      {
      if (comp->getOptions()->realTimeGC())
         {
         if (objectSize > fej9->getMaxObjectSizeForSizeClass())
            {
            doInline = false;
            }
         }
      }

   if (!isVariableLen)
      {
      allocateSize = (allocateSize + TR::Compiler->om.getObjectAlignmentInBytes() - 1) & (-TR::Compiler->om.getObjectAlignmentInBytes());
      }

   if (comp->compileRelocatableCode())
      {
      switch (opCode)
         {
         case TR::New: break;
         case TR::anewarray: break;
         case TR::newarray: break;
         default: doInline = false; break;
         }
      }

   static int count = 0;
   doInline = doInline && performTransformation(comp, "O^O <%3d> Inlining Allocation of %s [0x%p].\n", count++, node->getOpCode().getName(), node);

   if (doInline)
      {
      generateLabelInstruction(cg, TR::InstOpCode::label, node, generateLabelSymbol(cg));

      callLabel = generateLabelSymbol(cg);
      callReturnLabel = generateLabelSymbol(cg);
      doneLabel = generateLabelSymbol(cg);
      conditions = createConditionsAndPopulateVSXDeps(cg, 14);

      TR::Node *firstChild = node->getFirstChild();
      TR::Node *secondChild = NULL;

      if (opCode == TR::New)
         {
         // classReg is passed to the VM helper on the slow path and subsequently clobbered; copy it for later nodes if necessary
         classReg = cg->gprClobberEvaluate(firstChild);
         dataBegin = TR::Compiler->om.objectHeaderSizeInBytes();
         }
      else
         {
         TR_ASSERT(opCode == TR::newarray || opCode == TR::anewarray, "Bad opCode for VMnewEvaluator");
         isArray = true;
         if (generateArraylets || TR::Compiler->om.useHybridArraylets())
            {
            if (node->getOpCodeValue() == TR::newarray)
               elementSize = TR::Compiler->om.getSizeOfArrayElement(node);
            else if (comp->useCompressedPointers())
               elementSize = TR::Compiler->om.sizeofReferenceField();
            else
               elementSize = TR::Compiler->om.sizeofReferenceAddress();

            if (generateArraylets)
               dataBegin = fej9->getArrayletFirstElementOffset(elementSize, comp);
            else
               dataBegin = TR::Compiler->om.contiguousArrayHeaderSizeInBytes();
            static const char *p = feGetEnv("TR_TarokDataBeginBreak");
            if (p)
               TR_ASSERT(false, "Hitting Arraylet Data Begin Break");
            }
         else
            {
            dataBegin = TR::Compiler->om.contiguousArrayHeaderSizeInBytes();
            }
         secondChild = node->getSecondChild();
         enumReg = cg->evaluate(firstChild);

         insertType = (opCode == TR::newarray && secondChild->getDataType() == TR::Int32 && secondChild->getReferenceCount() == 1 && secondChild->getOpCode().isLoadConst());

         if (comp->compileRelocatableCode() && comp->getOption(TR_UseSymbolValidationManager))
            {
            // IMPORTANT: secondChild actually references the J9Class of the array *elements* rather
            // than the J9Class of the array itself; the new AOT infrastructure requires that
            // classReg contain the J9Class of the array, so we have to actually construct a new
            // loadaddr for that and then evaluate it instead of evaluating secondChild directly.
            // Note that the original secondChild *must still be evaluated* as it will be used in
            // the out-of-line code section.
            TR::StaticSymbol *classSymbol = TR::StaticSymbol::create(comp->trHeapMemory(), TR::Address);
            classSymbol->setStaticAddress(clazz);
            classSymbol->setClassObject();

            cg->evaluate(secondChild);
            secondChild = TR::Node::createWithSymRef(TR::loadaddr, 0,
               new (comp->trHeapMemory()) TR::SymbolReference(comp->getSymRefTab(), classSymbol));
            secondChild->incReferenceCount();

            classReg = cg->evaluate(secondChild);
            }
         else if (insertType)
            {
            classReg = cg->allocateRegister();
            }
         else
            {
            // classReg is passed to the VM helper on the slow path and subsequently clobbered; copy it for later nodes if necessary
            classReg = cg->gprClobberEvaluate(secondChild);
            }
         }
      TR::addDependency(conditions, classReg, TR::RealRegister::NoReg, TR_GPR, cg);
      conditions->getPostConditions()->getRegisterDependency(0)->setExcludeGPR0();

      if (secondChild == NULL)
         enumReg = cg->allocateRegister();
      TR::addDependency(conditions, enumReg, TR::RealRegister::NoReg, TR_GPR, cg);
      conditions->getPostConditions()->getRegisterDependency(1)->setExcludeGPR0();

      condReg = cg->allocateRegister(TR_CCR);
      TR::addDependency(conditions, condReg, TR::RealRegister::cr0, TR_CCR, cg);
      tmp6Reg = cg->allocateRegister();
      TR::addDependency(conditions, tmp6Reg, TR::RealRegister::NoReg, TR_GPR, cg);
      tmp4Reg = cg->allocateRegister();
      TR::addDependency(conditions, tmp4Reg, TR::RealRegister::gr11, TR_GPR, cg); // used by TR::PPCAllocPrefetchSnippet
      tmp5Reg = cg->allocateRegister();
      TR::addDependency(conditions, tmp5Reg, TR::RealRegister::NoReg, TR_GPR, cg);
      conditions->getPostConditions()->getRegisterDependency(5)->setExcludeGPR0();
      resReg = cg->allocateRegister();
      TR::addDependency(conditions, resReg, resultRealReg, TR_GPR, cg); // used by TR::PPCAllocPrefetchSnippet
      conditions->getPostConditions()->getRegisterDependency(6)->setExcludeGPR0();
      dataSizeReg = cg->allocateRegister();
      TR::addDependency(conditions, dataSizeReg, TR::RealRegister::NoReg, TR_GPR, cg);
      conditions->getPostConditions()->getRegisterDependency(7)->setExcludeGPR0();
      tmp7Reg = cg->allocateRegister();
      TR::addDependency(conditions, tmp7Reg, TR::RealRegister::NoReg, TR_GPR, cg);
      conditions->getPostConditions()->getRegisterDependency(8)->setExcludeGPR0();
      tmp3Reg = cg->allocateRegister();
      TR::addDependency(conditions, tmp3Reg, TR::RealRegister::gr10, TR_GPR, cg); // used by TR::PPCAllocPrefetchSnippet

      TR::Instruction *firstInstruction = cg->getAppendInstruction();

      if (needZeroInit)
         {
         zeroReg = cg->allocateRegister();
         TR::addDependency(conditions, zeroReg, TR::RealRegister::NoReg, TR_GPR, cg);
         }

      if (isVariableLen)
         allocateSize += dataBegin;

      //Here we set up backout paths if we overflow nonZeroTLH in genHeapAlloc.
      //If we overflow the nonZeroTLH, set the destination to the right VM runtime helper (eg jitNewObjectNoZeroInit, etc...)
      //The zeroed-TLH versions have their correct destinations already setup in TR_ByteCodeIlGenerator::genNew, TR_ByteCodeIlGenerator::genNewArray, TR_ByteCodeIlGenerator::genANewArray
      //To retrieve the destination node->getSymbolReference() is used below after genHeapAlloc.
      if (isDualTLH && node->canSkipZeroInitialization())
         {
         // For value types, the backout path should call jitNewValue helper call which is set up before code gen
         if ((node->getOpCodeValue() == TR::New)
             && (!TR::Compiler->om.areValueTypesEnabled() || (node->getSymbolReference() != comp->getSymRefTab()->findOrCreateNewValueSymbolRef(comp->getMethodSymbol()))))
            node->setSymbolReference(comp->getSymRefTab()->findOrCreateNewObjectNoZeroInitSymbolRef(comp->getMethodSymbol()));
         else if (node->getOpCodeValue() == TR::newarray)
            node->setSymbolReference(comp->getSymRefTab()->findOrCreateNewArrayNoZeroInitSymbolRef(comp->getMethodSymbol()));
         else if (node->getOpCodeValue() == TR::anewarray)
            node->setSymbolReference(comp->getSymRefTab()->findOrCreateANewArrayNoZeroInitSymbolRef(comp->getMethodSymbol()));
         }

      // On return, zeroReg is set to 0 if needZeroInit is true, and
      // dataSizeReg is set to the size of data area if isVariableLen
      // is true, and either elementSize != 1 or needZeroInit is true
      // (and we only ever use dataSizeReg below in those cases).
      genHeapAlloc(node, iCursor, clazz, isVariableLen, enumReg, classReg, resReg, zeroReg, condReg, dataSizeReg, tmp5Reg, tmp4Reg, tmp3Reg, tmp6Reg, tmp7Reg, callLabel, doneLabel, allocateSize, elementSize,
            usingTLH, needZeroInit, conditions, cg);

      // Call out to VM runtime helper if needed.
      TR::Register *objReg = cg->allocateCollectedReferenceRegister();
      TR_PPCOutOfLineCodeSection *outlinedHelperCall = new (cg->trHeapMemory()) TR_PPCOutOfLineCodeSection(node, TR::acall, objReg, callLabel, callReturnLabel, cg);
      cg->getPPCOutOfLineCodeSectionList().push_front(outlinedHelperCall);

      if (isArray)
         {
         // Align the array if necessary.
         if (doublewordAlign && !comp->getOptions()->realTimeGC())
            genAlignArray(node, iCursor, isVariableLen, resReg, objectSize, dataBegin, dataSizeReg, condReg, tmp5Reg, tmp4Reg, cg);
         if (comp->compileRelocatableCode() && (opCode == TR::anewarray || comp->getOption(TR_UseSymbolValidationManager)))
            genInitArrayHeader(node, iCursor, isVariableLen, clazz, classReg, resReg, zeroReg, condReg, enumReg, dataSizeReg, tmp5Reg, tmp4Reg, conditions, needZeroInit, cg);
         else
            genInitArrayHeader(node, iCursor, isVariableLen, clazz, NULL, resReg, zeroReg, condReg, enumReg, dataSizeReg,
                  tmp5Reg, tmp4Reg, conditions, needZeroInit, cg);

#ifdef J9VM_GC_SPARSE_HEAP_ALLOCATION
         if (TR::Compiler->om.isOffHeapAllocationEnabled())
            {
            /* Here we'll update dataAddr slot for both fixed and variable length arrays. Fixed length arrays are
             * simple as we just need to check first child of the node for array size. For variable length arrays,
             * runtime size checks are needed to determine whether to use contiguous or discontiguous header layout.
             *
             * In both scenarios, arrays of non-zero size use contiguous header layout while zero size arrays use
             * discontiguous header layout. DataAddr field of zero size arrays is intialized to NULL because they
             * don't have any data elements.
             */
            TR::Register *offsetReg = tmp5Reg;
            TR::Register *firstDataElementReg = tmp4Reg;
            TR::MemoryReference *dataAddrSlotMR = NULL;

            if (TR::Compiler->om.compressObjectReferences())
               {
               TR_ASSERT_FATAL_WITH_NODE(node,
                  (fej9->getOffsetOfDiscontiguousDataAddrField() - fej9->getOffsetOfContiguousDataAddrField()) == 8,
                  "Offset of dataAddr field in discontiguous array is expected to be 8 bytes more than contiguous array. "
                  "But was %d bytes for discontigous and %d bytes for contiguous array.\n",
                  fej9->getOffsetOfDiscontiguousDataAddrField(), fej9->getOffsetOfContiguousDataAddrField());
               }
            else
               {
               TR_ASSERT_FATAL_WITH_NODE(node,
                  fej9->getOffsetOfDiscontiguousDataAddrField() == fej9->getOffsetOfContiguousDataAddrField(),
                  "dataAddr field offset is expected to be same for both contiguous and discontiguous arrays in full refs. "
                  "But was %d bytes for discontiguous and %d bytes for contiguous array.\n",
                  fej9->getOffsetOfDiscontiguousDataAddrField(), fej9->getOffsetOfContiguousDataAddrField());
               }

            if (isVariableLen)
               {
               // We need to check enumReg at runtime to determine correct offset of dataAddr field.
               if (comp->getOption(TR_TraceCG))
                  traceMsg(comp, "Node (%p): Dealing with full/compressed refs variable length array.\n", node);

               TR::LabelSymbol *dataAddrInitDoneLabel = generateLabelSymbol(cg);
               TR::LabelSymbol *zeroArrayLabel = needZeroInit ? generateLabelSymbol(cg) : dataAddrInitDoneLabel;

               iCursor = generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::cmpli4, node, condReg, enumReg, 0, iCursor);
               iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, zeroArrayLabel, condReg, iCursor);

               // Load first data element address
               iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, firstDataElementReg, resReg, TR::Compiler->om.contiguousArrayHeaderSizeInBytes(), iCursor);
               dataAddrSlotMR = TR::MemoryReference::createWithDisplacement(cg, resReg, fej9->getOffsetOfContiguousDataAddrField(), 8);
               // Store the first data element address to dataAddr slot
               iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::std, node, dataAddrSlotMR, firstDataElementReg, iCursor);

               if (needZeroInit)
                  {
                  iCursor = generateLabelInstruction(cg, TR::InstOpCode::b, node, dataAddrInitDoneLabel);
                  // Init dataAddr field for zero size array
                  iCursor = generateLabelInstruction(cg, TR::InstOpCode::label, node, zeroArrayLabel, iCursor);
                  // Clear dataAddr field of 0 size array. Use zeroReg to clear the field
                  iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::std, node,
                     TR::MemoryReference::createWithDisplacement(cg, resReg, fej9->getOffsetOfDiscontiguousDataAddrField(), 8),
                     zeroReg, iCursor);
                  }

               iCursor = generateLabelInstruction(cg, TR::InstOpCode::label, node, dataAddrInitDoneLabel, iCursor);
               }
            else if (!isVariableLen && node->getFirstChild()->getOpCode().isLoadConst() && node->getFirstChild()->getInt() == 0)
               {
               if (comp->getOption(TR_TraceCG))
                  traceMsg(comp, "Node (%p): Dealing with full/compressed refs fixed length zero size array.\n", node);

               if (needZeroInit)
                  {
                  dataAddrSlotMR = TR::MemoryReference::createWithDisplacement(cg, resReg, fej9->getOffsetOfDiscontiguousDataAddrField(), 8);

                  // Use zeroReg to NULL dataAddr slot
                  iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::std, node, dataAddrSlotMR, zeroReg, iCursor);
                  }
               }
            else
               {
               if (comp->getOption(TR_TraceCG))
                  traceMsg(comp, "Node (%p): Dealing with full/compressed refs fixed length non-zero size array.\n", node);

               // Load first element address into firstDataElementReg
               iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, firstDataElementReg, resReg, TR::Compiler->om.contiguousArrayHeaderSizeInBytes(), iCursor);
               dataAddrSlotMR = TR::MemoryReference::createWithDisplacement(cg, resReg, fej9->getOffsetOfContiguousDataAddrField(), 8);

               // Store the first data element address to dataAddr slot
               iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::std, node, dataAddrSlotMR, firstDataElementReg, iCursor);
               }
            }
#endif /* J9VM_GC_SPARSE_HEAP_ALLOCATION */
         if (generateArraylets)
            {
            //write arraylet pointer to object header
            static const char *p = feGetEnv("TR_TarokPreWritePointerBreak");
            if (p)
               generateInstruction(cg, TR::InstOpCode::bad, node);

            iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, tmp4Reg, resReg, dataBegin, iCursor);
            if (TR::Compiler->om.compressedReferenceShiftOffset() > 0)
               iCursor = generateShiftRightLogicalImmediateLong(cg, node, tmp4Reg, tmp4Reg, TR::Compiler->om.compressedReferenceShiftOffset(), iCursor);
            iCursor = generateMemSrc1Instruction(cg, (comp->target().is64Bit() && !comp->useCompressedPointers()) ? TR::InstOpCode::std : TR::InstOpCode::stw, node,
                  TR::MemoryReference::createWithDisplacement(cg, resReg, fej9->getFirstArrayletPointerOffset(comp), comp->useCompressedPointers() ? 4 : TR::Compiler->om.sizeofReferenceAddress()), tmp4Reg, iCursor);
            static const char *p1 = feGetEnv("TR_TarokPostWritePointerBreak");
            if (p1)
               generateInstruction(cg, TR::InstOpCode::bad, node);
            }
         }
      else
         {
         genInitObjectHeader(node, iCursor, clazz, classReg, resReg, zeroReg, condReg, tmp5Reg, tmp4Reg, packedRegOffs, conditions, needZeroInit, cg);
         }

      //Do not need zero init if using dualTLH now, unless DisableBatchClear is set
      if (needZeroInit && (!isConstantZeroLenArrayAlloc))
         {
         // Perform initialization if it is needed:
         //   1) Initialize certain array elements individually. This depends on the optimizer
         //      providing a "short" list of individual indices;
         //   2) Initialize the whole array:
         //      a) If the object size is constant, we can choose strategy depending on the
         //         size of the array. Using straight line of code, or unrolled loop;
         //      b) For variable length of array, do a counted loop;

         TR_ExtraInfoForNew *initInfo = node->getSymbolReference()->getExtraInfo();

         static bool disableFastArrayZeroInit = (feGetEnv("TR_DisableFastArrayZeroInit") != NULL);

         if (!node->canSkipZeroInitialization() && (initInfo == NULL || initInfo->numZeroInitSlots > 0))
            {
            if (!isVariableLen)
               {
               if (initInfo != NULL && initInfo->zeroInitSlots != NULL && initInfo->numZeroInitSlots <= 9 && objectSize <= UPPER_IMMED)
                  {
                  TR_BitVectorIterator bvi(*initInfo->zeroInitSlots);

                  if (comp->target().is64Bit())
                     {
                     int32_t lastNotInit = -1;
                     int32_t currElem = -1;

                     // Try to group set of words into smallest groupings using double-word stores (where possible)
                     while (bvi.hasMoreElements())
                        {
                        currElem = bvi.getNextElement();

                        if (lastNotInit == -1)
                           {
                           // currently looking at only one slot (either just starting, or just emitted double-word store in last iter)
                           lastNotInit = currElem;
                           }
                        else if (currElem == lastNotInit + 1)
                           {
                           // consecutive slots, can use std
                           iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::std, node, TR::MemoryReference::createWithDisplacement(cg, resReg, lastNotInit * 4 + dataBegin, 8),
                                 zeroReg, iCursor);

                           lastNotInit = -1;
                           }
                        else
                           {
                           // Two non-consecutive slots, use stw for earlier slot and keep looking for slot
                           // consecutive with the second slot currently held
                           iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, resReg, lastNotInit * 4 + dataBegin, 4),
                                 zeroReg, iCursor);

                           lastNotInit = currElem;
                           }
                        }

                     if (lastNotInit != -1)
                        {
                        // Last slot is not consecutive with other slots, hasn't been initialized yet
                        iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, resReg, lastNotInit * 4 + dataBegin, 4),
                              zeroReg, iCursor);
                        }
                     }
                  else
                     {
                     while (bvi.hasMoreElements())
                        {
                        iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node,
                              TR::MemoryReference::createWithDisplacement(cg, resReg, bvi.getNextElement() * 4 + dataBegin, 4), zeroReg, iCursor);
                        }
                     }
                  }
               else if (objectSize <= (TR::Compiler->om.sizeofReferenceAddress() * 12))
                  {
                  for (idx = dataBegin; idx < (objectSize - TR::Compiler->om.sizeofReferenceAddress()); idx += TR::Compiler->om.sizeofReferenceAddress())
                     {
                     iCursor = generateMemSrc1Instruction(cg,TR::InstOpCode::Op_st, node, TR::MemoryReference::createWithDisplacement(cg, resReg, idx, TR::Compiler->om.sizeofReferenceAddress()), zeroReg,
                           iCursor);
                     }
                  if (idx + 4 == objectSize)
                     iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, resReg, idx, 4), zeroReg, iCursor);
                  else if (idx + 8 == objectSize)
                     iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::std, node, TR::MemoryReference::createWithDisplacement(cg, resReg, idx, 8), zeroReg, iCursor);
                  }
               else
                  {
                  static bool disableUnrollNewInitLoop1 = (feGetEnv("TR_DisableUnrollNewInitLoop1") != NULL);
                  if (!disableUnrollNewInitLoop1)
                     {
                     int32_t unrollFactor = 8;
                     int32_t width = comp->target().is64Bit() ? 8 : 4;
                     int32_t loopCount = (objectSize - dataBegin) / (unrollFactor * width);
                     int32_t res1 = (objectSize - dataBegin) % (unrollFactor * width);
                     int32_t residueCount = res1 / width;
                     int32_t res2 = res1 % width;
                     TR::LabelSymbol *loopStart = generateLabelSymbol(cg);

                     iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, tmp4Reg, resReg, dataBegin, iCursor);
                     if (loopCount > 0)
                        {
                        if (loopCount > 1)
                           {
                           iCursor = loadConstant(cg, node, loopCount, tmp5Reg, iCursor);
                           iCursor = generateSrc1Instruction(cg, TR::InstOpCode::mtctr, node, tmp5Reg, 0, iCursor);
                           iCursor = generateLabelInstruction(cg, TR::InstOpCode::label, node, loopStart, iCursor);
                           }

                        for (int32_t i = 0; i < unrollFactor; i++)
                           iCursor = generateMemSrc1Instruction(cg,TR::InstOpCode::Op_st, node, TR::MemoryReference::createWithDisplacement(cg, tmp4Reg, i * width, width), zeroReg, iCursor);

                        iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi2, node, tmp4Reg, tmp4Reg, (unrollFactor * width), iCursor);
                        if (loopCount > 1)
                           iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bdnz, node, loopStart, condReg, iCursor);
                        }

                     for (int32_t i = 0; i < residueCount; i++)
                        iCursor = generateMemSrc1Instruction(cg,TR::InstOpCode::Op_st, node, TR::MemoryReference::createWithDisplacement(cg, tmp4Reg, i * width, width), zeroReg, iCursor);

                     if (res2 && residueCount != 0)
                        iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi2, node, tmp4Reg, tmp4Reg, residueCount * width, iCursor);

                     if (comp->target().is64Bit() && res2 >= 4) // Should only be 0 or 4 here
                        iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, tmp4Reg, 0, 4), zeroReg, iCursor);
                     }
                  else
                     {
                     int32_t loopCnt = (objectSize - dataBegin) >> 4;
                     int32_t residue = ((objectSize - dataBegin) >> 2) & 0x03;
                     TR::LabelSymbol *initLoop = generateLabelSymbol(cg);

                     iCursor = loadConstant(cg, node, loopCnt, tmp5Reg, iCursor);
                     iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, tmp4Reg, resReg, dataBegin, iCursor);
                     iCursor = generateSrc1Instruction(cg, TR::InstOpCode::mtctr, node, tmp5Reg, 0, iCursor);
                     iCursor = generateLabelInstruction(cg, TR::InstOpCode::label, node, initLoop, iCursor);
                     iCursor = generateMemSrc1Instruction(cg,TR::InstOpCode::Op_st, node, TR::MemoryReference::createWithDisplacement(cg, tmp4Reg, 0, TR::Compiler->om.sizeofReferenceAddress()), zeroReg,
                           iCursor);
                     iCursor = generateMemSrc1Instruction(cg,TR::InstOpCode::Op_st, node,
                           TR::MemoryReference::createWithDisplacement(cg, tmp4Reg, TR::Compiler->om.sizeofReferenceAddress(), TR::Compiler->om.sizeofReferenceAddress()), zeroReg, iCursor);
                     if (comp->target().is32Bit())
                        {
                        iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, tmp4Reg, 8, 4), zeroReg, iCursor);
                        iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, tmp4Reg, 12, 4), zeroReg, iCursor);
                        }
                     iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, tmp4Reg, tmp4Reg, 16, iCursor);
                     iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bdnz, node, initLoop, condReg, iCursor);

                     idx = 0;
                     if (residue & 0x2)
                        {
                        iCursor = generateMemSrc1Instruction(cg,TR::InstOpCode::Op_st, node, TR::MemoryReference::createWithDisplacement(cg, tmp4Reg, 0, TR::Compiler->om.sizeofReferenceAddress()), zeroReg,
                              iCursor);

                        if (comp->target().is32Bit())
                           iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, tmp4Reg, 4, 4), zeroReg, iCursor);

                        idx = 8;
                        }
                     if (residue & 0x1)
                        {
                        iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, tmp4Reg, idx, 4), zeroReg, iCursor);
                        }
                     }
                  }
               }
            else // Init variable length array
               {
               static bool disableUnrollNewInitLoop2 = (feGetEnv("TR_DisableUnrollNewInitLoop2") != NULL);
               static bool disableNewZeroInit = (feGetEnv("TR_DisableNewZeroInit") != NULL);
               if (!disableUnrollNewInitLoop2)
                  {
                  int32_t unrollFactor = 8, i = 1;
                  int32_t width = comp->target().is64Bit() ? 8 : 4;
                  TR::LabelSymbol *loopLabel = generateLabelSymbol(cg);
                  TR::LabelSymbol *resLabel = generateLabelSymbol(cg);
                  TR::LabelSymbol *left4Label = generateLabelSymbol(cg);
                  TR::LabelSymbol *resLoopLabel = NULL;

                  if (!disableNewZeroInit)
                     resLoopLabel = generateLabelSymbol(cg);

                  iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, tmp5Reg, resReg, (dataBegin - width), iCursor);
                  iCursor = generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, tmp4Reg, tmp5Reg, dataSizeReg, iCursor);
                  iCursor = generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, condReg, tmp4Reg, tmp5Reg, iCursor);
                  iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, condReg, iCursor);

                  iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, tmp6Reg, tmp4Reg, (int32_t)(-unrollFactor * width), iCursor);

                  // Switch order of compare to avoid FXU reject in P6 for later store instruction
                  iCursor = generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, condReg, tmp6Reg, tmp5Reg, iCursor);
                  iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, resLabel, condReg, iCursor);

                  iCursor = generateLabelInstruction(cg, TR::InstOpCode::label, node, loopLabel, iCursor);
                  for (; i <= unrollFactor; i++)
                     iCursor = generateMemSrc1Instruction(cg, (width == 8) ? TR::InstOpCode::std : TR::InstOpCode::stw, node,
                           TR::MemoryReference::createWithDisplacement(cg, tmp5Reg, i * width, width), zeroReg, iCursor);
                  iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, tmp5Reg, tmp5Reg, (int32_t)(unrollFactor * width), iCursor);

                  iCursor = generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, condReg, tmp6Reg, tmp5Reg, iCursor);
                  iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bge, node, loopLabel, condReg, iCursor);

                  iCursor = generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, condReg, tmp4Reg, tmp5Reg, iCursor);
                  iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, condReg, iCursor);

                  iCursor = generateLabelInstruction(cg, TR::InstOpCode::label, node, resLabel, iCursor);

                  if (!disableNewZeroInit)
                     {
                     // End pointer-4
                     iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, tmp6Reg, tmp4Reg, -4, iCursor);

                     // Residue loop start
                     iCursor = generateLabelInstruction(cg, TR::InstOpCode::label, node, resLoopLabel, iCursor);

                     // If current pointer == end pointer - 4, there is 4 bytes left to write
                     // If current pointer > end pointer - 4, data is 8-byte divisible and we are done
                     iCursor = generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, condReg, tmp6Reg, tmp5Reg, iCursor);
                     iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, left4Label, condReg, iCursor);
                     iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, doneLabel, condReg, iCursor);

                     // Do 8-byte zero init and restart loop
                     iCursor = generateMemSrc1Instruction(cg,TR::InstOpCode::Op_st, node, TR::MemoryReference::createWithDisplacement(cg, tmp5Reg, width, width), zeroReg, iCursor);

                     if (comp->target().is32Bit())
                        {
                        iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, tmp5Reg, 8, 4), zeroReg, iCursor);
                        }

                     iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, tmp5Reg, tmp5Reg, 8, iCursor);
                     iCursor = generateLabelInstruction(cg, TR::InstOpCode::b, node, resLoopLabel);

                     // Finish last 4 bytes if necessary
                     iCursor = generateLabelInstruction(cg, TR::InstOpCode::label, node, left4Label, iCursor);
                     iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, tmp5Reg, width, 4), zeroReg, iCursor);
                     }
                  else
                     {
                     /* Need this in 64-bit as well */
                     iCursor = generateTrg1Src2Instruction(cg, TR::InstOpCode::subf, node, tmp6Reg, tmp5Reg, tmp4Reg, iCursor);
                     iCursor = generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpli, node, condReg, tmp6Reg, 4, iCursor);
                     iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, left4Label, condReg, iCursor);

                     iCursor = generateMemSrc1Instruction(cg,TR::InstOpCode::Op_st, node, TR::MemoryReference::createWithDisplacement(cg, tmp5Reg, 8, width), zeroReg, iCursor);

                     if (comp->target().is32Bit())
                        {
                        iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, tmp5Reg, 12, 4), zeroReg, iCursor);
                        }

                     iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, tmp5Reg, tmp5Reg, 8, iCursor);
                     iCursor = generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, condReg, tmp4Reg, tmp5Reg, iCursor);
                     iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, condReg, iCursor);
                     iCursor = generateLabelInstruction(cg, TR::InstOpCode::b, node, resLabel);

                     /* Need this in 64-bit as well */
                     iCursor = generateLabelInstruction(cg, TR::InstOpCode::label, node, left4Label, iCursor);
                     iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, tmp5Reg, 8, 4), zeroReg, iCursor);
                     }
                  }
               else
                  {
                  // Test for zero length array: the following two instructions will be combined
                  // to the record form by later pass.
                  iCursor = generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, tmp5Reg, dataSizeReg, 30, 0x3FFFFFFF, iCursor);
                  iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, condReg, tmp5Reg, 0, iCursor);
                  iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, condReg, iCursor);
                  iCursor = generateSrc1Instruction(cg, TR::InstOpCode::mtctr, node, tmp5Reg, 0, iCursor);
                  iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, tmp4Reg, resReg, dataBegin - 4, iCursor);

                  TR::LabelSymbol *initLoop = generateLabelSymbol(cg);
                  iCursor = generateLabelInstruction(cg, TR::InstOpCode::label, node, initLoop, iCursor);
                  iCursor = generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, tmp4Reg, 4, 4), zeroReg, iCursor);
                  iCursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, tmp4Reg, tmp4Reg, 4, iCursor);
                  iCursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::bdnz, node, initLoop, condReg, iCursor);
                  }
               }
            }
         }

      if (comp->compileRelocatableCode() && (opCode == TR::New || opCode == TR::anewarray))
         {
         firstInstruction = firstInstruction->getNext();
         TR_OpaqueClassBlock *classToValidate = clazz;

         TR_RelocationRecordInformation *recordInfo = (TR_RelocationRecordInformation *) comp->trMemory()->allocateMemory(sizeof(TR_RelocationRecordInformation), heapAlloc);
         recordInfo->data1 = allocateSize;
         recordInfo->data2 = node->getInlinedSiteIndex();
         recordInfo->data3 = (uintptr_t) callLabel;
         recordInfo->data4 = (uintptr_t) firstInstruction;

         TR::SymbolReference * classSymRef;
         TR_ExternalRelocationTargetKind reloKind;

         if (opCode == TR::New)
            {
            classSymRef = node->getFirstChild()->getSymbolReference();
            reloKind = TR_VerifyClassObjectForAlloc;
            }
         else
            {
            classSymRef = node->getSecondChild()->getSymbolReference();
            reloKind = TR_VerifyRefArrayForAlloc;

            if (comp->getOption(TR_UseSymbolValidationManager))
               classToValidate = fej9->getComponentClassFromArrayClass(classToValidate);
            }

         if (comp->getOption(TR_UseSymbolValidationManager))
            {
            TR_ASSERT_FATAL(classToValidate, "classToValidate should not be NULL, clazz=%p\n", clazz);
            recordInfo->data5 = (uintptr_t)classToValidate;
            }

         cg->addExternalRelocation(new (cg->trHeapMemory()) TR::BeforeBinaryEncodingExternalRelocation(firstInstruction, (uint8_t *) classSymRef, (uint8_t *) recordInfo, reloKind, cg),
                  __FILE__, __LINE__, node);
         }

      generateDepLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel, conditions);

      // At this point the object is initialized and we can move it to a collected register.
      // The out of line path will do the same. Since the live ranges of resReg and objReg
      // do not overlap we want them to occupy the same real register so that this instruction
      // becomes a nop and can be optimized away.
      generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, objReg, resReg);
      conditions = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(1, 1, cg->trMemory());
      TR::addDependency(conditions, objReg, resultRealReg, TR_GPR, cg);

      generateDepLabelInstruction(cg, TR::InstOpCode::label, node, callReturnLabel, conditions);

      cg->stopUsingRegister(condReg);
      cg->stopUsingRegister(tmp3Reg);
      cg->stopUsingRegister(tmp4Reg);
      cg->stopUsingRegister(tmp5Reg);
      cg->stopUsingRegister(tmp6Reg);
      cg->stopUsingRegister(tmp7Reg);
      cg->stopUsingRegister(resReg);
      cg->stopUsingRegister(dataSizeReg);
      if (needZeroInit)
         cg->stopUsingRegister(zeroReg);
      cg->decReferenceCount(firstChild);
      if (opCode == TR::New)
         {
         if (classReg != firstChild->getRegister())
            cg->stopUsingRegister(classReg);
         cg->stopUsingRegister(enumReg);
         }
      else
         {
         cg->decReferenceCount(secondChild);
         if (node->getSecondChild() != secondChild)
            cg->decReferenceCount(node->getSecondChild());
         if (classReg != secondChild->getRegister())
            cg->stopUsingRegister(classReg);
         }

      node->setRegister(objReg);
      return objReg;
      }
   else
      {
      TR::Node::recreate(node, TR::acall);
      callResult = directCallEvaluator(node, cg);
      TR::Node::recreate(node, opCode);
      return (callResult);
      }
   }

// Special case evaluator for simple read monitors.
static bool simpleReadMonitor(TR::Node *node, TR::CodeGenerator *cg, TR::Node *objNode, TR::Register *objReg, TR::Register *objectClassReg, TR::Register *condReg,
      TR::Register *lookupOffsetReg)
   {
#if defined(J9VM_JIT_NEW_DUAL_HELPERS)
   // This needs to be updated to work with dual-mode helpers by not using snippets
   return false;
#endif
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());
   static char *disableSRM = feGetEnv("TR_disableSimpleReadMonitors");
   static char *nolwsync = feGetEnv("TR_noSRMlwsync");

   if (disableSRM)
      return false;

   // look for the special case of a read monitor sequence that protects
   // a single fixed-point load, ie:
   //   monenter (object)
   //   a simple form of aloadi or iloadi
   //   monexit (object)

   // note: before we make the following checks it is important that the
   // object on the monenter has been evaluated (currently done before the
   // call to this routine, and the result passed in as objReg)

   if (!node->isReadMonitor())
      return false;

   TR::TreeTop *nextTreeTop = cg->getCurrentEvaluationTreeTop()->getNextTreeTop();
   if (!nextTreeTop)
      return false;

   TR::TreeTop *liveMonitorExitStore = NULL;
   TR::TreeTop *liveMonitorEnterStore = NULL;

   if (nextTreeTop->getNode()->getOpCode().isStore() && nextTreeTop->getNode()->getSymbol()->holdsMonitoredObject())
      {
      liveMonitorEnterStore = nextTreeTop;
      nextTreeTop = nextTreeTop->getNextTreeTop();
      }

   if (nextTreeTop->getNode()->getOpCode().getOpCodeValue() == TR::monexitfence)
      {
      liveMonitorExitStore = nextTreeTop;
      nextTreeTop = nextTreeTop->getNextTreeTop();
      }

   TR::TreeTop *secondNextTreeTop = nextTreeTop->getNextTreeTop();
   if (!secondNextTreeTop)
      return false;

   if (secondNextTreeTop->getNode()->getOpCode().getOpCodeValue() == TR::monexitfence)
      {
      liveMonitorExitStore = secondNextTreeTop;
      secondNextTreeTop = secondNextTreeTop->getNextTreeTop();
      }

   // check that the second node after the monent is the matching monexit
   TR::Node *secondNextTopNode = secondNextTreeTop->getNode();
   if (!secondNextTopNode)
      return false;
   if (secondNextTopNode->getOpCodeValue() == TR::treetop)
      secondNextTopNode = secondNextTopNode->getFirstChild();
   if (secondNextTopNode->getOpCodeValue() != TR::monexit || secondNextTopNode->getFirstChild() != objNode)
      return false;

   // check that the first node after the monent is a simple load
   TR::Node *nextTopNode = nextTreeTop->getNode();
   if (!nextTopNode)
      return false;
   if (nextTopNode->getOpCodeValue() == TR::treetop || nextTopNode->getOpCodeValue() == TR::iRegStore)
      nextTopNode = nextTopNode->getFirstChild();
   if (!TR::LoadStoreHandler::isSimpleLoad(cg, nextTopNode))
      return false;
   // possible TODO: expand the set of load types we can handle
   if (nextTopNode->getOpCodeValue() != TR::aloadi && nextTopNode->getOpCodeValue() != TR::iloadi)
      {
      // printf("Rejecting read-only monitor on load type in %s\n", comp->getCurrentMethod()->signature());
      return false;
      }
   // possible TODO: expand the complexity of loads we can handle
   // aloadi and iloadi are indirect and have a child
   // if we don't need to evaluate that child then the aloadi or iloadi
   // consists of a single hardware instruction and satisfies our current
   // constraint of simple
   if (!nextTopNode->getFirstChild()->getRegister())
      {
      // printf("Rejecting read-only monitor on complex load in %s\n", comp->getCurrentMethod()->signature());
      return false;
      }

   // end of checks, we can handle this case
   // printf("Success on read-only monitor optimization in %s\n", comp->getCurrentMethod()->signature());

   // RAS: dump out the load and monexit trees, since we are effectively
   // evaluating them here
   if (comp->getOption(TR_TraceCG) || debug("traceGRA"))
      {
      trfprintf(comp->getOutFile(), "\n");
      comp->getDebug()->dumpSingleTreeWithInstrs(nextTreeTop, NULL, true, false, true, comp->getOption(TR_TraceRegisterPressureDetails));
      trfprintf(comp->getOutFile(), "\n");
      comp->getDebug()->dumpSingleTreeWithInstrs(secondNextTreeTop, NULL, true, false, true, comp->getOption(TR_TraceRegisterPressureDetails));
      trfflush(comp->getOutFile());
      }

   if (liveMonitorEnterStore)
      {
      TR::TreeTop *oldCur = cg->getCurrentEvaluationTreeTop();
      cg->setCurrentEvaluationTreeTop(liveMonitorEnterStore);
      cg->evaluate(liveMonitorEnterStore->getNode());
      cg->setCurrentEvaluationTreeTop(oldCur);
      }
   // check the TR::monexit to see if an lwsync is required
   // or whether to suppress the lwsync for performance analysis purposes
   bool needlwsync = comp->target().isSMP() && !secondNextTopNode->canSkipSync() && nolwsync == NULL;

   // generate code for the combined monenter, load and monexit:
   //    <monitor object evaluation code>
   //    <TR_MemoryReference evaluation code for load>
   // (see design 908 for more information on the code sequences used)
   // if running on a POWER4/5 and the load is from the monitor object:
   //    (altsequence)
   //   if an lwsync is needed:
   //          li     offsetReg, offset_of_monitor_word
   //       loopLabel:
   //          lwarx  monitorReg, [objReg, offsetReg]
   //          cmpli  cndReg, monitorReg, 0
   //          bne    cndReg, recurCheckLabel
   //          stwcx  [objReg, offsetReg], metaReg
   //          bne-   loopLabel
   //          or     loadBaseReg,loadBaseReg,monitorReg
   //          <load>
   //          lwsync
   //          stw    offsetReg(objReg), monitorReg
   //       restartLabel:
   //   else (no lwsync):
   //          li     offsetReg, offset_of_monitor_word
   //       loopLabel:
   //          lwarx  monitorReg, [objReg, offsetReg]
   //          cmpli  cndReg, monitorReg, 0
   //          bne    cndReg, recurCheckLabel
   //          stwcx  [objReg, offsetReg], metaReg
   //          bne-   loopLabel
   //          or     loadBaseReg,loadBaseReg,monitorReg
   //          <load>
   //          xor    monitorReg,loadResultReg,loadResultReg
   //          stw    offsetReg(objReg), monitorReg
   //       restartLabel:
   // else:
   //   if an lwsync is needed:
   //          li     offsetReg, offset_of_monitor_word
   //       loopLabel:
   //          lwarx  monitorReg, [objReg, offsetReg]
   //          cmpli  cndReg, monitorReg, 0
   //          bne    cndReg, recurCheckLabel
   //          or     loadBaseReg,loadBaseReg,monitorReg
   //          <load>
   //          lwsync
   //          stwcx  [objReg, offsetReg], monitorReg
   //          bne-   loopLabel
   //       restartLabel:
   //   else (no lwsync):
   //          li     offsetReg, offset_of_monitor_word
   //       loopLabel:
   //          lwarx  monitorReg, [objReg, offsetReg]
   //          cmpli  cndReg, monitorReg, 0
   //          bne    cndReg, recurCheckLabel
   //          or     loadBaseReg,loadBaseReg,monitorReg
   //          <load>
   //          xor    monitorReg,loadResultReg,loadResultReg
   //          stwcx  [objReg, offsetReg], monitorReg
   //          bne-   loopLabel
   //       restartLabel:
   // === SNIPPET === (generated later)
   //   recurCheckLabel:
   //      rlwinm monitorReg, monitorReg, 0, THREAD_MASK_CONST
   //      cmp    cndReg, metaReg, monitorReg
   //      bne    cndReg, slowPath
   //      <load>
   //      b      restartLabel
   //   slowPath:
   //      bl     monitorEnterHelper
   //      <load>
   //      bl     monitorExitHelper
   //      b      restartLabel

   TR::RegisterDependencyConditions *conditions;
   TR::Register *metaReg, *monitorReg, *cndReg, *offsetReg;
   TR::InstOpCode::Mnemonic opCode;
   int32_t lockSize;

   int32_t numDeps = 6;
   if (objectClassReg)
      numDeps = numDeps + 2;

   if (lookupOffsetReg)
      numDeps = numDeps + 1;

   conditions = createConditionsAndPopulateVSXDeps(cg, numDeps);

   monitorReg = cg->allocateRegister();
   offsetReg = cg->allocateRegister();
   cndReg = cg->allocateRegister(TR_CCR);
   TR::addDependency(conditions, monitorReg, TR::RealRegister::gr11, TR_GPR, cg);
   TR::addDependency(conditions, offsetReg, TR::RealRegister::NoReg, TR_GPR, cg);
   TR::addDependency(conditions, cndReg, TR::RealRegister::cr0, TR_CCR, cg);

   // the following code is derived from the aloadi and iloadi evaluators
   TR::Register *loadResultReg;
   OMR::Power::NodeMemoryReference tempMR;
   if (nextTopNode->getOpCodeValue() == TR::aloadi)
      loadResultReg = cg->allocateCollectedReferenceRegister();
   else
      loadResultReg = cg->allocateRegister();
   if (nextTopNode->getSymbolReference()->getSymbol()->isInternalPointer())
      {
      loadResultReg->setPinningArrayPointer(nextTopNode->getSymbolReference()->getSymbol()->castToInternalPointerAutoSymbol()->getPinningArrayPointer());
      loadResultReg->setContainsInternalPointer();
      }
   nextTopNode->setRegister(loadResultReg);
   TR::InstOpCode::Mnemonic loadOpCode;
   if (nextTopNode->getOpCodeValue() == TR::aloadi && comp->target().is64Bit())
      {
      if (TR::Compiler->om.compressObjectReferences() && nextTopNode->getSymbol()->isClassObject())
         {
         tempMR = TR::LoadStoreHandler::generateSimpleLoadMemoryReference(cg, nextTopNode, 4);
         loadOpCode = TR::InstOpCode::lwz;
         }
      else
         {
         tempMR = TR::LoadStoreHandler::generateSimpleLoadMemoryReference(cg, nextTopNode, 8);
         loadOpCode = TR::InstOpCode::ld;
         }
      }
   else
      {
      tempMR = TR::LoadStoreHandler::generateSimpleLoadMemoryReference(cg, nextTopNode, 4);
      loadOpCode = TR::InstOpCode::lwz;
      }
   // end of code derived from the aloadi and iloadi evaluators

   TR::addDependency(conditions, loadResultReg, TR::RealRegister::NoReg, TR_GPR, cg);
   if (tempMR.getMemoryReference()->getBaseRegister() != objReg)
      {
      TR::addDependency(conditions, tempMR.getMemoryReference()->getBaseRegister(), TR::RealRegister::NoReg, TR_GPR, cg);
      conditions->getPreConditions()->getRegisterDependency(4)->setExcludeGPR0();
      conditions->getPostConditions()->getRegisterDependency(4)->setExcludeGPR0();
      }
   TR::addDependency(conditions, objReg, TR::RealRegister::gr3, TR_GPR, cg);

   TR::Register *baseReg = objReg;
   if (objectClassReg)
      baseReg = objectClassReg;

   bool altsequence = (comp->target().cpu.is(OMR_PROCESSOR_PPC_GP) || comp->target().cpu.is(OMR_PROCESSOR_PPC_GR)) && tempMR.getMemoryReference()->getBaseRegister() == objReg;

   TR::LabelSymbol *loopLabel, *restartLabel, *recurCheckLabel, *monExitCallLabel;
   loopLabel = generateLabelSymbol(cg);
   restartLabel = generateLabelSymbol(cg);
   recurCheckLabel = generateLabelSymbol(cg);
   monExitCallLabel = generateLabelSymbol(cg);

   metaReg = cg->getMethodMetaDataRegister();

   if (comp->target().is64Bit() && !fej9->generateCompressedLockWord())
      {
      opCode = TR::InstOpCode::ldarx;
      lockSize = 8;
      }
   else
      {
      opCode = TR::InstOpCode::lwarx;
      lockSize = 4;
      }

   if (objectClassReg)
      generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, offsetReg, 0);
   else
      generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, offsetReg, fej9->getByteOffsetToLockword((TR_OpaqueClassBlock *) cg->getMonClass(node)));
   generateLabelInstruction(cg, TR::InstOpCode::label, node, loopLabel);
   generateTrg1MemInstruction(cg, opCode, node, monitorReg, TR::MemoryReference::createWithIndexReg(cg, baseReg, offsetReg, lockSize));
   generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpli, node, cndReg, monitorReg, 0);
   TR::Instruction *gcPoint = generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, recurCheckLabel, cndReg);
   gcPoint->PPCNeedsGCMap(0xFFFFFFFF);

   if (altsequence)
      {
      if (comp->target().is64Bit() && !fej9->generateCompressedLockWord())
         opCode = TR::InstOpCode::stdcx_r;
      else
         opCode = TR::InstOpCode::stwcx_r;
      generateMemSrc1Instruction(cg, opCode, node, TR::MemoryReference::createWithIndexReg(cg, baseReg, offsetReg, lockSize), metaReg);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, PPCOpProp_BranchUnlikely, node, loopLabel, cndReg);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::OR, node, tempMR.getMemoryReference()->getBaseRegister(), tempMR.getMemoryReference()->getBaseRegister(), monitorReg);
      generateTrg1MemInstruction(cg, loadOpCode, node, loadResultReg, tempMR.getMemoryReference());
      if (needlwsync)
         generateInstruction(cg, TR::InstOpCode::lwsync, node);
      else
         generateTrg1Src2Instruction(cg, TR::InstOpCode::XOR, node, monitorReg, loadResultReg, loadResultReg);
      if (comp->target().is64Bit() && !fej9->generateCompressedLockWord())
         opCode = TR::InstOpCode::std;
      else
         opCode = TR::InstOpCode::stw;
      generateMemSrc1Instruction(cg, opCode, node,
            TR::MemoryReference::createWithDisplacement(cg, baseReg, objectClassReg ? 0 : fej9->getByteOffsetToLockword((TR_OpaqueClassBlock *) cg->getMonClass(node)), lockSize), monitorReg);
      }
   else
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::OR, node, tempMR.getMemoryReference()->getBaseRegister(), tempMR.getMemoryReference()->getBaseRegister(), monitorReg);
      generateTrg1MemInstruction(cg, loadOpCode, node, loadResultReg, tempMR.getMemoryReference());
      if (needlwsync)
         generateInstruction(cg, TR::InstOpCode::lwsync, node);
      else
         generateTrg1Src2Instruction(cg, TR::InstOpCode::XOR, node, monitorReg, loadResultReg, loadResultReg);
      if (comp->target().is64Bit() && !fej9->generateCompressedLockWord())
         opCode = TR::InstOpCode::stdcx_r;
      else
         opCode = TR::InstOpCode::stwcx_r;
      generateMemSrc1Instruction(cg, opCode, node, TR::MemoryReference::createWithIndexReg(cg, baseReg, offsetReg, lockSize), monitorReg);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, PPCOpProp_BranchUnlikely, node, loopLabel, cndReg);
      }

   if (objectClassReg)
      TR::addDependency(conditions, objectClassReg, TR::RealRegister::NoReg, TR_GPR, cg);

   if (lookupOffsetReg)
      TR::addDependency(conditions, lookupOffsetReg, TR::RealRegister::NoReg, TR_GPR, cg);

   if (condReg)
      TR::addDependency(conditions, condReg, TR::RealRegister::cr1, TR_CCR, cg);

   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, restartLabel, conditions);

   conditions->stopUsingDepRegs(cg, objReg, loadResultReg);
   cg->decReferenceCount(objNode);

   TR::Snippet *snippet = new (cg->trHeapMemory()) TR::PPCReadMonitorSnippet(cg, node, secondNextTopNode, recurCheckLabel, monExitCallLabel, restartLabel, loadOpCode,
         tempMR.getMemoryReference()->getOffset(), objectClassReg);
   cg->addSnippet(snippet);

   // the load and monexit trees need reference count adjustments and
   // must not be evaluated
   tempMR.decReferenceCounts(cg);
   cg->decReferenceCount(nextTopNode);
   if (secondNextTopNode == secondNextTreeTop->getNode())
      cg->recursivelyDecReferenceCount(secondNextTopNode->getFirstChild());
   else
      cg->recursivelyDecReferenceCount(secondNextTopNode);

   if (liveMonitorExitStore)
      {
      TR::TreeTop *prev = liveMonitorExitStore->getPrevTreeTop();
      TR::TreeTop *next = liveMonitorExitStore->getNextTreeTop();
      prev->join(next);

      next = secondNextTreeTop->getNextTreeTop();
      secondNextTreeTop->join(liveMonitorExitStore);
      liveMonitorExitStore->join(next);
      }

   cg->setCurrentEvaluationTreeTop(secondNextTreeTop);

   return true;
   }

void J9::Power::TreeEvaluator::generateCheckForValueMonitorEnterOrExit(TR::Node *node, TR::LabelSymbol *helperCallLabel, TR::Register *objReg, TR::Register *objectClassReg, TR::Register *temp1Reg, TR::Register *temp2Reg, TR::Register *condReg, TR::CodeGenerator *cg, int32_t classFlag)
{
   //get class of object
   generateLoadJ9Class(node, objectClassReg, objReg, cg);

   //get memory reference to class flags
   TR_J9VMBase *fej9 = (TR_J9VMBase *)(cg->fe());
   TR::MemoryReference *classFlagsMemRef = TR::MemoryReference::createWithDisplacement(cg, objectClassReg, static_cast<uintptr_t>(fej9->getOffsetOfClassFlags()), 4);

   generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, temp1Reg, classFlagsMemRef);

   loadConstant(cg, node, classFlag, temp2Reg);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::and_r, node, temp1Reg, temp1Reg, temp2Reg);

   //If obj is value type or value based class instance, call VM helper and throw IllegalMonitorState exception, else continue as usual
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, helperCallLabel, condReg);
}

TR::Register *J9::Power::TreeEvaluator::VMmonentEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());
   int32_t lwOffset = fej9->getByteOffsetToLockword((TR_OpaqueClassBlock *) cg->getMonClass(node));
   TR_ASSERT(lwOffset>=LOWER_IMMED && lwOffset<=UPPER_IMMED, "Need re-work on using lwOffset.");
   TR_YesNoMaybe isMonitorValueBasedOrValueType = cg->isMonitorValueBasedOrValueType(node);

   if (comp->getOption(TR_MimicInterpreterFrameShape) ||
         (comp->getOption(TR_FullSpeedDebug) && node->isSyncMethodMonitor()) ||
         (isMonitorValueBasedOrValueType == TR_yes) ||
         comp->getOption(TR_DisableInlineMonEnt))
      {
      TR::ILOpCodes opCode = node->getOpCodeValue();
      TR::Node::recreate(node, TR::call);
      TR::Register *targetRegister = directCallEvaluator(node, cg);
      TR::Node::recreate(node, opCode);
      return targetRegister;
      }

   int32_t numDeps = 6;

   TR::Node *objNode = node->getFirstChild();
   TR::Register *objReg = cg->evaluate(objNode);

   TR::Register *monitorReg = cg->allocateRegister();
   TR::Register *offsetReg = cg->allocateRegister();
   TR::Register *tempReg = cg->allocateRegister();

   TR::Register *objectClassReg = cg->allocateRegister();
   TR::Register *lookupOffsetReg = NULL;

   TR::Register *condReg = cg->allocateRegister(TR_CCR);
   TR::Register *metaReg = cg->getMethodMetaDataRegister();

   TR::Register *baseReg = objReg;

   if (lwOffset <= 0)
      {
      if (comp->getOption(TR_EnableMonitorCacheLookup))
         {
         lookupOffsetReg = cg->allocateRegister();
         numDeps++;
         }
      }

   TR::RegisterDependencyConditions *conditions;
   conditions = createConditionsAndPopulateVSXDeps(cg, numDeps);

   TR::addDependency(conditions, objReg, TR::RealRegister::NoReg, TR_GPR, cg);
   conditions->getPreConditions()->getRegisterDependency(conditions->getAddCursorForPre() - 1)->setExcludeGPR0();
   conditions->getPostConditions()->getRegisterDependency(conditions->getAddCursorForPost() - 1)->setExcludeGPR0();

   TR::addDependency(conditions, monitorReg, TR::RealRegister::NoReg, TR_GPR, cg);
   conditions->getPreConditions()->getRegisterDependency(conditions->getAddCursorForPre() - 1)->setExcludeGPR0();
   conditions->getPostConditions()->getRegisterDependency(conditions->getAddCursorForPost() - 1)->setExcludeGPR0();

   TR::addDependency(conditions, offsetReg, TR::RealRegister::NoReg, TR_GPR, cg);

   TR::addDependency(conditions, tempReg, TR::RealRegister::NoReg, TR_GPR, cg);

   TR::addDependency(conditions, objectClassReg, TR::RealRegister::NoReg, TR_GPR, cg);
   conditions->getPreConditions()->getRegisterDependency(conditions->getAddCursorForPre() - 1)->setExcludeGPR0();
   conditions->getPostConditions()->getRegisterDependency(conditions->getAddCursorForPost() - 1)->setExcludeGPR0();

   if (lookupOffsetReg)
      {
      TR::addDependency(conditions, lookupOffsetReg, TR::RealRegister::NoReg, TR_GPR, cg);
      conditions->getPreConditions()->getRegisterDependency(conditions->getAddCursorForPre() - 1)->setExcludeGPR0();
      conditions->getPostConditions()->getRegisterDependency(conditions->getAddCursorForPost() - 1)->setExcludeGPR0();
      }

   TR::addDependency(conditions, condReg, TR::RealRegister::cr0, TR_CCR, cg);

   TR::LabelSymbol *callLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *monitorLookupCacheLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *fallThruFromMonitorLookupCacheLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *startLabel = generateLabelSymbol(cg);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, startLabel, NULL);
   startLabel->setStartInternalControlFlow();

   //If object is not known to be value type or value based class at compile time, check at run time
   if (isMonitorValueBasedOrValueType == TR_maybe)
      {
      generateCheckForValueMonitorEnterOrExit(node, callLabel, objReg, objectClassReg, tempReg, offsetReg, condReg, cg, J9_CLASS_DISALLOWS_LOCKING_FLAGS);
      }

   bool simpleLocking = false;

   if (lwOffset <= 0)
      {
      generateLoadJ9Class(node, objectClassReg, objReg, cg);

      int32_t offsetOfLockOffset = offsetof(J9Class, lockOffset);
      TR::MemoryReference *tempMR = TR::MemoryReference::createWithDisplacement(cg, objectClassReg, offsetOfLockOffset, TR::Compiler->om.sizeofReferenceAddress());
      generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, objectClassReg, tempMR);

      generateTrg1Src1ImmInstruction(cg, comp->target().is64Bit() ? TR::InstOpCode::cmpi8 : TR::InstOpCode::cmpi4, node, condReg, objectClassReg, 0);

      if (comp->getOption(TR_EnableMonitorCacheLookup))
         {
         lwOffset = 0;
         generateConditionalBranchInstruction(cg, TR::InstOpCode::ble, node, monitorLookupCacheLabel, condReg);
         generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, objectClassReg, objReg, objectClassReg);
         generateLabelInstruction(cg, TR::InstOpCode::b, node, fallThruFromMonitorLookupCacheLabel);

         generateLabelInstruction(cg, TR::InstOpCode::label, node, monitorLookupCacheLabel);

         int32_t offsetOfMonitorLookupCache = offsetof(J9VMThread, objectMonitorLookupCache);

         generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, lookupOffsetReg, objReg);

         int32_t t = trailingZeroes(TR::Compiler->om.getObjectAlignmentInBytes());

         if (comp->target().is64Bit())
            generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::sradi, node, lookupOffsetReg, lookupOffsetReg, t);
         else
            generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::srawi, node, lookupOffsetReg, lookupOffsetReg, t);

         J9JavaVM * jvm = fej9->getJ9JITConfig()->javaVM;
         if (comp->target().is64Bit())
            simplifyANDRegImm(node, lookupOffsetReg, lookupOffsetReg, (int64_t) J9VMTHREAD_OBJECT_MONITOR_CACHE_SIZE - 1, cg, objNode);
         else
            simplifyANDRegImm(node, lookupOffsetReg, lookupOffsetReg, J9VMTHREAD_OBJECT_MONITOR_CACHE_SIZE - 1, cg, objNode);

         if (comp->target().is64Bit())
            generateShiftLeftImmediateLong(cg, node, lookupOffsetReg, lookupOffsetReg, trailingZeroes((int32_t) TR::Compiler->om.sizeofReferenceField()));
         else
            generateShiftLeftImmediate(cg, node, lookupOffsetReg, lookupOffsetReg, trailingZeroes((int32_t) TR::Compiler->om.sizeofReferenceField()));

         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, lookupOffsetReg, lookupOffsetReg, offsetOfMonitorLookupCache);

         generateTrg1MemInstruction(cg, (comp->target().is64Bit() && fej9->generateCompressedLockWord()) ? TR::InstOpCode::lwz :TR::InstOpCode::Op_load, node, objectClassReg,
               TR::MemoryReference::createWithIndexReg(cg, metaReg, lookupOffsetReg, TR::Compiler->om.sizeofReferenceField()));

         generateTrg1Src1ImmInstruction(cg, comp->target().is64Bit() ? TR::InstOpCode::cmpi8 : TR::InstOpCode::cmpi4, node, condReg, objectClassReg, 0);
         generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, callLabel, condReg);

         int32_t offsetOfMonitor = offsetof(J9ObjectMonitor, monitor);
         generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, lookupOffsetReg,
               TR::MemoryReference::createWithDisplacement(cg, objectClassReg, offsetOfMonitor, TR::Compiler->om.sizeofReferenceAddress()));

         int32_t offsetOfUserData = offsetof(J9ThreadAbstractMonitor, userData);
         generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, lookupOffsetReg,
               TR::MemoryReference::createWithDisplacement(cg, lookupOffsetReg, offsetOfUserData, TR::Compiler->om.sizeofReferenceAddress()));

         generateTrg1Src2Instruction(cg, comp->target().is64Bit() ? TR::InstOpCode::cmp8 : TR::InstOpCode::cmp4, node, condReg, lookupOffsetReg, objReg);
         generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, callLabel, condReg);

         int32_t offsetOfAlternateLockWord = offsetof(J9ObjectMonitor, alternateLockword);

         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, objectClassReg, objectClassReg, offsetOfAlternateLockWord);

         generateLabelInstruction(cg, TR::InstOpCode::label, node, fallThruFromMonitorLookupCacheLabel);
         }
      else
         {
         generateConditionalBranchInstruction(cg, TR::InstOpCode::ble, node, callLabel, condReg);
         generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, objectClassReg, objReg, objectClassReg);
         }

      simpleLocking = true;
      lwOffset = 0;
      baseReg = objectClassReg;
      }

   /* If the -XX:-GlobalLockReservation option is set, try to use the original aggressive reserved locking code path. */
   if (!fej9->isEnableGlobalLockReservationSet())
      {
      bool reserveLocking = false, normalLockWithReservationPreserving = false;

      if (!simpleLocking && comp->getOption(TR_ReservingLocks))
         TR::TreeEvaluator::evaluateLockForReservation(node, &reserveLocking, &normalLockWithReservationPreserving, cg);

      if (reserveLocking)
         return reservationLockEnter(node, lwOffset, cg, conditions, baseReg, monitorReg, offsetReg, tempReg, condReg, callLabel);

      if (!simpleLocking && !reserveLocking && !normalLockWithReservationPreserving && simpleReadMonitor(node, cg, objNode, objReg, objectClassReg, condReg, lookupOffsetReg))
         return NULL;
      }

   int32_t lockSize;
   TR::InstOpCode::Mnemonic loadOpCode, storeOpCode, reservedLoadOpCode, conditionalStoreOpCode, compareLogicalOpCode, compareLogicalImmOpCode;

   if (comp->target().is64Bit() && !fej9->generateCompressedLockWord())
      {
      lockSize = 8;
      loadOpCode = TR::InstOpCode::ld;
      storeOpCode = TR::InstOpCode::std;
      reservedLoadOpCode = TR::InstOpCode::ldarx;
      conditionalStoreOpCode = TR::InstOpCode::stdcx_r;
      compareLogicalOpCode = TR::InstOpCode::cmpl8;
      compareLogicalImmOpCode = TR::InstOpCode::cmpli8;
      }
   else
      {
      lockSize = 4;
      loadOpCode = TR::InstOpCode::lwz;
      storeOpCode = TR::InstOpCode::stw;
      reservedLoadOpCode = TR::InstOpCode::lwarx;
      conditionalStoreOpCode = TR::InstOpCode::stwcx_r;
      compareLogicalOpCode = TR::InstOpCode::cmpl4;
      compareLogicalImmOpCode = TR::InstOpCode::cmpli4;
      }

   // full codegen support for read monitors is not enabled, pending performance tuning
   /*
    * Read only locks do not support either the Reserved bit nor the Learning bit.
    * They do not support ownedMonitorCount updates either.
    * They are disabled until the code path is updated.
    */
   if (true || !ppcSupportsReadMonitors || !node->isReadMonitor())
      {
      /*
       * JIT locking fast path checks:
       * 1. Compare lockword to 0. If equal, use CAS to set TID in lockword.
       * 2. Check if TID matches, highest RC bit is 0, Learning bit is clear and Inflated bit is clear. If so, increment RC.
       * 3. All fast path checks failed so go to VM.
       *
       * Check 1 catches Flat-Unlocked.
       * Check 2 catches Reserved-Unlocked, Reserved-Locked and Flat-Locked.
       * Check 3 catches New-PreLearning, New-AutoReserve, Learning-Unlocked, Learning-Locked and Inflated and sends them to the VM.
       *
       *
       *    ld/lwz monitorReg, lwOffset(baseReg)
       *    cmpli  cr0, monitorReg, 0
       *    bne    incrementCheckLabel
       *    li     offsetReg, lwOffset
       *
       * loopLabel:
       *    larx   monitorReg, [baseReg, offsetReg]
       *    cmpli  cr0, monitorReg, 0
       *    bne    callLabel
       *    stcx.  metaReg, [baseReg, offsetReg]
       *    bne    loopLabel
       *    isync
       *    b      doneLabel
       *
       * incrementCheckLabel:
       *    li     tempReg, LOCK_NON_PRIMITIVE_ENTER_IGNORE_MASK
       *    andc   tempReg, monitorReg, tempReg
       *    cmpl   cr0, tempReg, metaReg
       *    bne    callLabel
       *    addi   monitorReg, monitorReg, LOCK_INC_DEC_VALUE
       *    st     monitorReg, lwOffset(baseReg)
       *
       * doneLabel:
       * #if JAVA_SPEC_VERSION >= 19
       *    ld/lwz tempReg, OwnedMonitorCountOffset(metaReg)
       *    addi   tempReg, tempReg, 1
       *    st     tempReg, OwnedMonitorCountOffset(metaReg)
       * #endif //JAVA_SPEC_VERSION >= 19
       * callReturnLabel:
       * === OUT OF LINE ===
       * callLabel:
       *    bl    jitMonitorEntry
       *    b     callReturnLabel
       */

      TR::LabelSymbol *loopLabel, *incrementCheckLabel, *doneLabel;
      loopLabel = generateLabelSymbol(cg);
      incrementCheckLabel = generateLabelSymbol(cg);
      doneLabel = generateLabelSymbol(cg);

      generateTrg1MemInstruction(cg, loadOpCode, node, monitorReg, TR::MemoryReference::createWithDisplacement(cg, baseReg, lwOffset, lockSize));

      generateTrg1Src1ImmInstruction(cg, compareLogicalImmOpCode, node, condReg, monitorReg, 0);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, incrementCheckLabel, condReg);
      generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, offsetReg, lwOffset);

      generateLabelInstruction(cg, TR::InstOpCode::label, node, loopLabel);
      generateTrg1MemInstruction(cg, reservedLoadOpCode, PPCOpProp_LoadReserveExclusiveAccess, node, monitorReg, TR::MemoryReference::createWithIndexReg(cg, baseReg, offsetReg, lockSize));
      generateTrg1Src1ImmInstruction(cg, compareLogicalImmOpCode, node, condReg, monitorReg, 0);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, callLabel, condReg);
      generateMemSrc1Instruction(cg, conditionalStoreOpCode, node, TR::MemoryReference::createWithIndexReg(cg, baseReg, offsetReg, lockSize), metaReg);

      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, PPCOpProp_BranchUnlikely, node, loopLabel, condReg);

      if (comp->target().isSMP())
         {
         // either an isync or a sync can be used here to prevent any
         // following loads from executing out-of-order
         // on nstar and pulsar a sync is cheaper, while an isync is
         // cheaper on other processors
         if (comp->target().cpu.is(OMR_PROCESSOR_PPC_NSTAR) || comp->target().cpu.is(OMR_PROCESSOR_PPC_PULSAR))
            generateInstruction(cg, TR::InstOpCode::sync, node);
         else
            generateInstruction(cg, TR::InstOpCode::isync, node);
         }
      generateLabelInstruction(cg, TR::InstOpCode::b, node, doneLabel);

      generateLabelInstruction(cg, TR::InstOpCode::label, node, incrementCheckLabel);
      generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, tempReg, LOCK_NON_PRIMITIVE_ENTER_IGNORE_MASK);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::andc, node, tempReg, monitorReg, tempReg);
      generateTrg1Src2Instruction(cg, compareLogicalOpCode, node, condReg, tempReg, metaReg);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, callLabel, condReg);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, monitorReg, monitorReg, LOCK_INC_DEC_VALUE);
      generateMemSrc1Instruction(cg, storeOpCode, node, TR::MemoryReference::createWithDisplacement(cg, baseReg, lwOffset, lockSize), monitorReg);

      generateLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel);
#if JAVA_SPEC_VERSION >= 19
      /*
       * Adjust J9VMThread ownedMonitorCount for execution paths that do not
       * go out of line to acquire the lock. It is safe and efficient to do
       * this unconditionally. There is no need to check for overflow.
       */
      generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, tempReg, TR::MemoryReference::createWithDisplacement(cg, metaReg, fej9->thisThreadGetOwnedMonitorCountOffset(), TR::Compiler->om.sizeofReferenceAddress()));
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, tempReg, tempReg, 1);
      generateMemSrc1Instruction(cg, TR::InstOpCode::Op_st, node, TR::MemoryReference::createWithDisplacement(cg, metaReg, fej9->thisThreadGetOwnedMonitorCountOffset(), TR::Compiler->om.sizeofReferenceAddress()), tempReg);
#endif //JAVA_SPEC_VERSION >= 19

      TR::LabelSymbol *callReturnLabel = generateLabelSymbol(cg);

      TR_PPCOutOfLineCodeSection *outlinedHelperCall = new (cg->trHeapMemory()) TR_PPCOutOfLineCodeSection(node, TR::call, NULL, callLabel, callReturnLabel, cg);
      cg->getPPCOutOfLineCodeSectionList().push_front(outlinedHelperCall);

      generateDepLabelInstruction(cg, TR::InstOpCode::label, node, callReturnLabel, conditions);
      callReturnLabel->setEndInternalControlFlow();

      conditions->stopUsingDepRegs(cg, objReg);
      cg->decReferenceCount(objNode);
      }
   else
      {
      // read-only locks, readerReg = 0x0000 0000
      //    li     offsetReg, offset_of_monitor_word
      // loopLabel:
      //    larx   monitorReg, [baseReg, offsetReg]
      //    rlwinm tempReg, monitorReg, 0, 0xFFFFFF83
      //    cmpi   cr0, tempReg, 0x0
      //    bne    callLabel
      //    addi   monitorReg, monitorReg, 4
      //    stcx.  [baseReg, offsetReg], monitorReg
      //    bne    loopLabel
      //    isync
      // doneLabel:
      // callReturnLabel:
      // === OUT OF LINE ===
      // callLabel:
      //    bl     jitMonitorEntry
      //    b      callReturnLabel

      TR::LabelSymbol *doneLabel, *loopLabel;
      doneLabel = generateLabelSymbol(cg);
      loopLabel = generateLabelSymbol(cg);

      generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, offsetReg, lwOffset);
      generateDepLabelInstruction(cg, TR::InstOpCode::label, node, loopLabel, conditions);
      generateTrg1MemInstruction(cg, reservedLoadOpCode, PPCOpProp_LoadReserveExclusiveAccess, node, monitorReg,
            TR::MemoryReference::createWithIndexReg(cg, baseReg, offsetReg, lockSize));
      generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, tempReg, monitorReg, 0, 0xFFFFFF80);
      generateTrg1Src1ImmInstruction(cg, compareLogicalImmOpCode, node, condReg, tempReg, 0);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, callLabel, condReg);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, monitorReg, monitorReg, 4);
      generateMemSrc1Instruction(cg, conditionalStoreOpCode, node, TR::MemoryReference::createWithIndexReg(cg, baseReg, offsetReg, lockSize), monitorReg);

      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, PPCOpProp_BranchUnlikely, node, loopLabel, condReg);

      if (comp->target().isSMP())
         {
         // either an isync or a sync can be used here to prevent any
         // following loads from executing out-of-order
         // on nstar and pulsar a sync is cheaper, while an isync is
         // cheaper on other processors
         if (comp->target().cpu.is(OMR_PROCESSOR_PPC_NSTAR) || comp->target().cpu.is(OMR_PROCESSOR_PPC_PULSAR))
            generateInstruction(cg, TR::InstOpCode::sync, node);
         else
            generateInstruction(cg, TR::InstOpCode::isync, node);
         }
      generateDepLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel, conditions);

      doneLabel->setEndInternalControlFlow();

      TR::LabelSymbol *callReturnLabel = generateLabelSymbol(cg);

      TR_PPCOutOfLineCodeSection *outlinedHelperCall = new (cg->trHeapMemory()) TR_PPCOutOfLineCodeSection(node, TR::call, NULL, callLabel, callReturnLabel, cg);
      cg->getPPCOutOfLineCodeSectionList().push_front(outlinedHelperCall);

      generateLabelInstruction(cg, TR::InstOpCode::label, node, callReturnLabel);

      conditions->stopUsingDepRegs(cg, objReg);
      cg->decReferenceCount(objNode);
      }

   return (NULL);
   }

TR::Register *J9::Power::TreeEvaluator::VMarrayCheckEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());
   TR::Register *obj1Reg, *obj2Reg, *tmp1Reg, *tmp2Reg, *cndReg;
   TR::LabelSymbol *doneLabel, *snippetLabel;
   TR::Instruction *gcPoint;
   TR::Snippet *snippet;
   TR::RegisterDependencyConditions *conditions;
   int32_t depIndex;
   TR::Compilation* comp = cg->comp();

   obj1Reg = cg->evaluate(node->getFirstChild());
   obj2Reg = cg->evaluate(node->getSecondChild());
   doneLabel = generateLabelSymbol(cg);
   conditions = createConditionsAndPopulateVSXDeps(cg, 5);
   depIndex = 0;
   nonFixedDependency(conditions, obj1Reg, &depIndex, TR_GPR, true, cg);
   nonFixedDependency(conditions, obj2Reg, &depIndex, TR_GPR, true, cg);
   tmp1Reg = nonFixedDependency(conditions, NULL, &depIndex, TR_GPR, true, cg);
   tmp2Reg = nonFixedDependency(conditions, NULL, &depIndex, TR_GPR, false, cg);
   cndReg = cg->allocateRegister(TR_CCR);
   TR::addDependency(conditions, cndReg, TR::RealRegister::cr0, TR_CCR, cg);

   // We have a unique snippet sharing arrangement in this code sequence.
   // It is not generally applicable for other situations.
   snippetLabel = NULL;

   // Same array, we are done.
   //
   generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, cndReg, obj1Reg, obj2Reg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, cndReg);

   // If we know nothing about either object, test object1 first. It has to be an array.
   //
   if (!node->isArrayChkPrimitiveArray1() && !node->isArrayChkReferenceArray1() && !node->isArrayChkPrimitiveArray2() && !node->isArrayChkReferenceArray2())
      {

      generateLoadJ9Class(node, tmp1Reg, obj1Reg, cg);

      generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, tmp1Reg, TR::MemoryReference::createWithDisplacement(cg, tmp1Reg, (int32_t) offsetof(J9Class, classDepthAndFlags), 4));

      loadConstant(cg, node, (int32_t) J9AccClassRAMArray, tmp2Reg);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::AND, node, tmp2Reg, tmp1Reg, tmp2Reg);

      generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpi, node, cndReg, tmp2Reg, NULLVALUE);

      snippetLabel = generateLabelSymbol(cg);
      gcPoint = generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, snippetLabel, cndReg);
      snippet = new (cg->trHeapMemory()) TR::PPCHelperCallSnippet(cg, node, snippetLabel, node->getSymbolReference(), doneLabel);
      cg->addSnippet(snippet);
      }

   // One of the object is array. Test equality of two objects' classes.
   //
   generateLoadJ9Class(node, tmp2Reg, obj2Reg, cg);
   generateLoadJ9Class(node, tmp1Reg, obj1Reg, cg);

   generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, cndReg, tmp1Reg, tmp2Reg);

   // If either object is known to be of primitive component type,
   // we are done: since both of them have to be of equal class.
   if (node->isArrayChkPrimitiveArray1() || node->isArrayChkPrimitiveArray2())
      {
      if (snippetLabel == NULL)
         {
         snippetLabel = generateLabelSymbol(cg);
         gcPoint = generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, snippetLabel, cndReg);
         snippet = new (cg->trHeapMemory()) TR::PPCHelperCallSnippet(cg, node, snippetLabel, node->getSymbolReference(), doneLabel);
         cg->addSnippet(snippet);
         }
      else
         generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, snippetLabel, cndReg);
      }
   // We have to take care of the un-equal class situation: both of them must be of reference array
   else
      {
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, cndReg);

      // Object1 must be of reference component type, otherwise throw exception
      if (!node->isArrayChkReferenceArray1())
         {

         // Loading the Class Pointer -> classDepthAndFlags
         generateLoadJ9Class(node, tmp1Reg, obj1Reg, cg);

         generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, tmp1Reg, TR::MemoryReference::createWithDisplacement(cg, tmp1Reg, (int32_t) offsetof(J9Class, classDepthAndFlags), 4));

         // We already have classDepth&Flags in tmp1Reg.  X = (ramclass->ClassDepthAndFlags)>>J9AccClassRAMShapeShift
         generateShiftRightLogicalImmediate(cg, node, tmp1Reg, tmp1Reg, J9AccClassRAMShapeShift);

         // We need to perform a X & OBJECT_HEADER_SHAPE_MASK

         generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, tmp2Reg, tmp1Reg, 0, OBJECT_HEADER_SHAPE_MASK);
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, cndReg, tmp2Reg, OBJECT_HEADER_SHAPE_POINTERS);

         if (snippetLabel == NULL)
            {
            snippetLabel = generateLabelSymbol(cg);
            gcPoint = generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, snippetLabel, cndReg);
            snippet = new (cg->trHeapMemory()) TR::PPCHelperCallSnippet(cg, node, snippetLabel, node->getSymbolReference(), doneLabel);
            cg->addSnippet(snippet);
            }
         else
            generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, snippetLabel, cndReg);
         }

      // Object2 must be of reference component type array, otherwise throw exception
      if (!node->isArrayChkReferenceArray2())
         {

         generateLoadJ9Class(node, tmp1Reg, obj2Reg, cg);
         generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, tmp1Reg, TR::MemoryReference::createWithDisplacement(cg, tmp1Reg, (int32_t) offsetof(J9Class, classDepthAndFlags), 4));

         loadConstant(cg, node, (int32_t) J9AccClassRAMArray, tmp2Reg);
         generateTrg1Src2Instruction(cg, TR::InstOpCode::AND, node, tmp2Reg, tmp1Reg, tmp2Reg);

         TR::Instruction * i = generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpi, node, cndReg, tmp2Reg, NULLVALUE);

         if (snippetLabel == NULL)
            {
            snippetLabel = generateLabelSymbol(cg);
            gcPoint = generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, snippetLabel, cndReg);
            snippet = new (cg->trHeapMemory()) TR::PPCHelperCallSnippet(cg, node, snippetLabel, node->getSymbolReference(), doneLabel);
            cg->addSnippet(snippet);
            }
         else
            generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, snippetLabel, cndReg);

         // We already have classDepth&Flags in tmp1Reg.  X = (ramclass->ClassDepthAndFlags)>>J9AccClassRAMShapeShift
         generateShiftRightLogicalImmediate(cg, node, tmp1Reg, tmp1Reg, J9AccClassRAMShapeShift);

         // We need to perform a X & OBJECT_HEADER_SHAPE_MASK

         generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, tmp2Reg, tmp1Reg, 0, OBJECT_HEADER_SHAPE_MASK);
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, cndReg, tmp2Reg, OBJECT_HEADER_SHAPE_POINTERS);
         generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, snippetLabel, cndReg);
         }
      }

   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel, conditions);
   if (snippetLabel != NULL)
      {
      gcPoint->PPCNeedsGCMap(0x0);
      snippet->gcMap().setGCRegisterMask(0x0);
      }

   conditions->stopUsingDepRegs(cg, obj1Reg, obj2Reg);

   cg->decReferenceCount(node->getFirstChild());
   cg->decReferenceCount(node->getSecondChild());
   return (NULL);
   }

void J9::Power::TreeEvaluator::genArrayCopyWithArrayStoreCHK(TR::Node* node, TR::CodeGenerator *cg)
   {
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());
   TR::Register *metaReg = cg->getMethodMetaDataRegister();
   TR::Register *gr3Reg, *gr2Reg, *lengthReg, *temp1Reg;

   //      child 0 ------  Source array object;
   //      child 1 ------  Destination array object;
   //      child 2 ------  Source byte address;
   //      child 3 ------  Destination byte address;
   //      child 4 ------  Copy length in byte;

   bool aix_style_linkage = (comp->target().isAIX() || (comp->target().is64Bit() && comp->target().isLinux()));
   J9::Power::JNILinkage *jniLinkage = (J9::Power::JNILinkage*) cg->getLinkage(TR_J9JNILinkage);
   const TR::PPCLinkageProperties &pp = jniLinkage->getProperties();
   TR::RegisterDependencyConditions *conditions = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(pp.getNumberOfDependencyGPRegisters(),
         pp.getNumberOfDependencyGPRegisters(), cg->trMemory());

   // I_32 referenceArrayCopy(
   //                   J9VMThread *vmThread,
   //                   J9IndexableObjectContiguous *srcObject,
   //                   J9IndexableObjectContiguous *destObject,
   //                   U_8 *srcAddress,
   //                   U_8 *destAddress,
   //                   I_32 lengthInSlots)
   intptr_t *funcdescrptr = (intptr_t*) fej9->getReferenceArrayCopyHelperAddress();

   TR::Instruction *iCursor;
   if (aix_style_linkage)
      {
      gr2Reg = cg->allocateRegister();
      TR::addDependency(conditions, gr2Reg, TR::RealRegister::gr2, TR_GPR, cg);
      }
   // set up the arguments and dependencies
   int32_t argSize = jniLinkage->buildJNIArgs(node, conditions, pp, true);
   temp1Reg = conditions->searchPreConditionRegister(TR::RealRegister::gr12);
   if (aix_style_linkage &&
      !(comp->target().is64Bit() && comp->target().isLinux() && comp->target().cpu.isLittleEndian()))
      {
      intptr_t target_ip = funcdescrptr[0];
      intptr_t target_toc = funcdescrptr[1];
      iCursor = loadAddressConstant(cg, comp->compileRelocatableCode(), node, target_ip, temp1Reg, NULL, false, TR_ArrayCopyHelper);
      iCursor = loadAddressConstant(cg, comp->compileRelocatableCode(), node, target_toc, gr2Reg, iCursor, false, TR_ArrayCopyToc);
      }
   else
      {
      bool doRelocation = comp->compileRelocatableCode();
#ifdef J9VM_OPT_JITSERVER
      doRelocation = doRelocation || comp->isOutOfProcessCompilation();
#endif
      iCursor = loadAddressConstant(cg, doRelocation, node, (intptr_t) funcdescrptr, temp1Reg, NULL, false, TR_ArrayCopyHelper);
      }

   iCursor = generateSrc1Instruction(cg, TR::InstOpCode::mtctr, node, temp1Reg, 0, iCursor);
   // the C routine expects length measured by slots
   lengthReg = conditions->searchPreConditionRegister(TR::RealRegister::gr8);
   int32_t elementSize;
   if (comp->useCompressedPointers())
      elementSize = TR::Compiler->om.sizeofReferenceField();
   else
      elementSize = (int32_t) TR::Compiler->om.sizeofReferenceAddress();
   generateShiftRightLogicalImmediate(cg, node, lengthReg, lengthReg, trailingZeroes(elementSize));
   // pass vmThread as the first parameter
   gr3Reg = cg->allocateRegister();
   TR::addDependency(conditions, gr3Reg, TR::RealRegister::gr3, TR_GPR, cg);
   iCursor = generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, gr3Reg, metaReg, iCursor);
   // call the C routine
   TR::Instruction *gcPoint = generateDepInstruction(cg, TR::InstOpCode::bctrl, node, conditions);
   gcPoint->PPCNeedsGCMap(pp.getPreservedRegisterMapForGC());
   // check return value
   TR::Register *cr0Reg = conditions->searchPreConditionRegister(TR::RealRegister::cr0);
   generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpi, node, cr0Reg, gr3Reg, -1);
   // throw exception if needed
   TR::SymbolReference *throwSymRef = comp->getSymRefTab()->findOrCreateArrayStoreExceptionSymbolRef(comp->getJittedMethodSymbol());
   TR::LabelSymbol *exceptionSnippetLabel = cg->lookUpSnippet(TR::Snippet::IsHelperCall, throwSymRef);
   if (exceptionSnippetLabel == NULL)
      {
      exceptionSnippetLabel = generateLabelSymbol(cg);
      cg->addSnippet(new (cg->trHeapMemory()) TR::PPCHelperCallSnippet(cg, node, exceptionSnippetLabel, throwSymRef));
      }

   gcPoint = generateDepConditionalBranchInstruction(cg, TR::InstOpCode::bnel, node, exceptionSnippetLabel, cr0Reg, conditions->cloneAndFix(cg));
   // somewhere to hang the dependencies
   TR::LabelSymbol *depLabel = generateLabelSymbol(cg);
   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, depLabel, conditions);
   gcPoint->PPCNeedsGCMap(pp.getPreservedRegisterMapForGC());
   cg->machine()->setLinkRegisterKilled(true);
   conditions->stopUsingDepRegs(cg);
   cg->setHasCall();
   return;
   }

void J9::Power::TreeEvaluator::genWrtbarForArrayCopy(TR::Node *node, TR::Register *srcObjReg, TR::Register *dstObjReg, TR::CodeGenerator *cg)
   {
   TR::Compilation *comp = cg->comp();
   bool ageCheckIsNeeded = false;
   bool cardMarkIsNeeded = false;
   auto gcMode = TR::Compiler->om.writeBarrierType();
   TR::RegisterDependencyConditions *conditions = NULL;

   ageCheckIsNeeded = (gcMode == gc_modron_wrtbar_oldcheck || gcMode == gc_modron_wrtbar_cardmark_and_oldcheck || gcMode == gc_modron_wrtbar_always);
   cardMarkIsNeeded = (gcMode == gc_modron_wrtbar_cardmark || gcMode == gc_modron_wrtbar_cardmark_incremental);

   if (!ageCheckIsNeeded && !cardMarkIsNeeded)
      return;

   if (ageCheckIsNeeded)
      {
      TR::Register *temp1Reg;
      TR::Register *temp2Reg;
      TR::Register *condReg = cg->allocateRegister(TR_CCR);
      TR::Instruction *gcPoint;

      if (gcMode != gc_modron_wrtbar_always)
         {
         temp1Reg = cg->allocateRegister();
         temp2Reg = cg->allocateRegister();
         conditions = createConditionsAndPopulateVSXDeps(cg, 4);
         TR::addDependency(conditions, temp1Reg, TR::RealRegister::gr11, TR_GPR, cg);
         TR::addDependency(conditions, temp2Reg, TR::RealRegister::NoReg, TR_GPR, cg);
         }
      else
         {
         conditions = createConditionsAndPopulateVSXDeps(cg, 2);
         }

      TR::addDependency(conditions, dstObjReg, TR::RealRegister::gr3, TR_GPR, cg);
      TR::addDependency(conditions, condReg, TR::RealRegister::cr0, TR_CCR, cg);

      TR::LabelSymbol *doneLabel;
      TR::SymbolReference *wbRef = comp->getSymRefTab()->findOrCreateWriteBarrierBatchStoreSymbolRef(comp->getMethodSymbol());

      if (gcMode != gc_modron_wrtbar_always)
         {
         doneLabel = generateLabelSymbol(cg);

         TR::Register *metaReg = cg->getMethodMetaDataRegister();

         // temp1Reg = dstObjReg - heapBaseForBarrierRange0
         generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp1Reg,
               TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, heapBaseForBarrierRange0), TR::Compiler->om.sizeofReferenceAddress()));
         generateTrg1Src2Instruction(cg, TR::InstOpCode::subf, node, temp1Reg, temp1Reg, dstObjReg);

         // if (temp1Reg >= heapSizeForBarrierRage0), object not in the tenured area
         generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, temp2Reg,
               TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, heapSizeForBarrierRange0), TR::Compiler->om.sizeofReferenceAddress()));
         generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, condReg, temp1Reg, temp2Reg);
         generateConditionalBranchInstruction(cg, TR::InstOpCode::bge, node, doneLabel, condReg);
         }

      gcPoint = generateDepImmSymInstruction(cg, TR::InstOpCode::bl, node, (uintptr_t) wbRef->getSymbol()->castToMethodSymbol()->getMethodAddress(),
            new (cg->trHeapMemory()) TR::RegisterDependencyConditions((uint8_t) 0, 0, cg->trMemory()), wbRef, NULL);

      if (gcMode != gc_modron_wrtbar_always)
         generateDepLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel, conditions);

      cg->machine()->setLinkRegisterKilled(true);
      cg->setHasCall();

      // This GC point can only happen when there is an exception. As a result, we can ditch
      // all registers.
      gcPoint->PPCNeedsGCMap(0xFFFFFFFF);
      }

   if (!ageCheckIsNeeded && cardMarkIsNeeded)
      {
      if (!comp->getOptions()->realTimeGC())

         {
         TR::Register *cndReg = cg->allocateRegister(TR_CCR);
         TR::Register *temp1Reg = cg->allocateRegister();
         TR::Register *temp2Reg = cg->allocateRegister();
         TR::Register *temp3Reg = cg->allocateRegister();
         conditions = createConditionsAndPopulateVSXDeps(cg, 7);

         TR::addDependency(conditions, cndReg, TR::RealRegister::cr0, TR_CCR, cg);
         TR::addDependency(conditions, dstObjReg, TR::RealRegister::NoReg, TR_GPR, cg);
         TR::addDependency(conditions, temp1Reg, TR::RealRegister::NoReg, TR_GPR, cg);
         conditions->getPostConditions()->getRegisterDependency(1)->setExcludeGPR0();
         conditions->getPostConditions()->getRegisterDependency(2)->setExcludeGPR0();
         TR::addDependency(conditions, temp2Reg, TR::RealRegister::NoReg, TR_GPR, cg);
         TR::addDependency(conditions, temp3Reg, TR::RealRegister::NoReg, TR_GPR, cg);

         VMCardCheckEvaluator(node, dstObjReg, cndReg, temp1Reg, temp2Reg, temp3Reg, conditions, cg);
         generateDepLabelInstruction(cg, TR::InstOpCode::label, node, generateLabelSymbol(cg), conditions);

         }
      else
         TR_ASSERT(0, "genWrtbarForArrayCopy card marking not supported for RT");
      }
      if (conditions)
         {
         conditions->stopUsingDepRegs(cg, dstObjReg);
         }
   }

static TR::Register *genCAS(TR::Node *node, TR::CodeGenerator *cg, TR::Register *objReg, TR::Register *offsetReg, TR::Register *oldVReg, TR::Register *newVReg, TR::Register *cndReg,
      TR::LabelSymbol *doneLabel, TR::Node *objNode, int32_t oldValue, bool oldValueInReg, int32_t dataSize, bool isReference, bool isExchange, bool casWithoutSync)
   {
   TR::Compilation *comp = cg->comp();
   TR::Register *resultReg;

   if (isReference && isExchange)
      {
      resultReg = cg->allocateCollectedReferenceRegister();
      }
   else
      {
      resultReg = cg->allocateRegister();
      }

   TR::InstOpCode::Mnemonic reservedLoadOpCode, conditionalStoreOpCode, compareOpCode, compareImmOpCode;
   switch (dataSize)
      {
      case 4:
         reservedLoadOpCode = TR::InstOpCode::lwarx;
         conditionalStoreOpCode = TR::InstOpCode::stwcx_r;
         compareOpCode = TR::InstOpCode::cmp4;
         compareImmOpCode = TR::InstOpCode::cmpi4;
         break;
      case 8:
         reservedLoadOpCode = TR::InstOpCode::ldarx;
         conditionalStoreOpCode = TR::InstOpCode::stdcx_r;
         compareOpCode = TR::InstOpCode::cmp8;
         compareImmOpCode = TR::InstOpCode::cmpi8;
         break;
      default:
         TR_ASSERT_FATAL_WITH_NODE(node, false, "Unknown dataSize: %d\n", dataSize);
         break;
      }

   // Memory barrier --- NOTE: we should be able to do a test upfront to save this barrier,
   //                          but Hursley advised to be conservative due to lack of specification.
   generateInstruction(cg, TR::InstOpCode::lwsync, node);

   TR::LabelSymbol *loopLabel = generateLabelSymbol(cg);
   generateLabelInstruction(cg, TR::InstOpCode::label, node, loopLabel);

   generateTrg1MemInstruction(cg, reservedLoadOpCode, node, resultReg, TR::MemoryReference::createWithIndexReg(cg, objReg, offsetReg, dataSize));
   if (oldValueInReg)
      generateTrg1Src2Instruction(cg, compareOpCode, node, cndReg, resultReg, oldVReg);
   else
      generateTrg1Src1ImmInstruction(cg, compareImmOpCode, node, cndReg, resultReg, oldValue);

   if (!isExchange)
      {
      generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, 0);
      }
   else if (isReference && comp->target().is64Bit() && comp->useCompressedPointers())
      {
      genDecompressPointer(cg, node, resultReg);
      }

   // We don't know how the compare will fare such that we don't dictate the prediction
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, doneLabel, cndReg);

   generateMemSrc1Instruction(cg, conditionalStoreOpCode, node, TR::MemoryReference::createWithIndexReg(cg, objReg, offsetReg, dataSize), newVReg);
   // We expect this store is usually successful, i.e., the following branch will not be taken
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, PPCOpProp_BranchUnlikely, node, loopLabel, cndReg);

   // We deviate from the VM helper here: no-store-no-barrier instead of always-barrier
   if (!casWithoutSync)
      generateInstruction(cg, TR::InstOpCode::sync, node);

   if (!isExchange)
      {
      generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, 1);
      }

   node->setRegister(resultReg);
   return resultReg;
   }

static TR::Register *VMinlineCompareAndSetOrExchange(TR::Node *node, TR::CodeGenerator *cg, int32_t dataSize, bool isExchange)
   {
   TR::Compilation * comp = cg->comp();
   TR::Register *objReg, *offsetReg, *oldVReg, *newVReg, *resultReg, *cndReg;
   TR::Node *firstChild, *objNode, *offsetNode, *oldVNode, *newVNode;
   TR::RegisterDependencyConditions *conditions;
   TR::LabelSymbol *startLabel, *doneLabel;
   int64_t offsetValue, oldValue;
   bool oldValueInReg = true, freeOffsetReg = false;
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (cg->fe());

   firstChild = node->getFirstChild();
   objNode = node->getSecondChild();
   offsetNode = node->getChild(2);
   oldVNode = node->getChild(3);
   newVNode = node->getChild(4);

   objReg = cg->evaluate(objNode);

   // VM helper chops off the value in 32bit, and we don't want the whole long value either
   if (offsetNode->getOpCode().isLoadConst() && offsetNode->getRegister() == NULL && comp->target().is32Bit())
      {
      offsetValue = offsetNode->getLongInt();
      offsetReg = cg->allocateRegister();
      loadConstant(cg, node, (int32_t) offsetValue, offsetReg);
      freeOffsetReg = true;
      }
   else
      {
      offsetReg = cg->evaluate(offsetNode);
      freeOffsetReg = false;

      // Assume that the offset is positive and not pathologically large (i.e., > 2^31).
      if (comp->target().is32Bit())
         offsetReg = offsetReg->getLowOrder();
      }

   if (oldVNode->getOpCode().isLoadConst() && oldVNode->getRegister() == NULL)
      {
      switch (dataSize)
         {
         case 4:
            oldValue = oldVNode->getInt();
            break;
         case 8:
            oldValue = oldVNode->getLongInt();
            break;
         default:
            TR_ASSERT_FATAL_WITH_NODE(node, false, "Unknown dataSize: %d\n", dataSize);
            break;
         }

      if (oldValue >= LOWER_IMMED && oldValue <= UPPER_IMMED)
         {
         oldValueInReg = false;
         }
      }

   if (oldValueInReg)
      {
      oldVReg = cg->evaluate(oldVNode);
      }

   newVReg = cg->evaluate(newVNode);
   cndReg = cg->allocateRegister(TR_CCR);
   startLabel = generateLabelSymbol(cg);
   doneLabel = generateLabelSymbol(cg);

   bool casWithoutSync = false;
   TR_OpaqueMethodBlock *caller = node->getOwningMethod();
   if (caller)
      {
      TR_ResolvedMethod *m = fej9->createResolvedMethod(cg->trMemory(), caller, node->getSymbolReference()->getOwningMethod(comp));
      if ((m->getRecognizedMethod() == TR::java_util_concurrent_atomic_AtomicInteger_weakCompareAndSet)
            || (m->getRecognizedMethod() == TR::java_util_concurrent_atomic_AtomicLong_weakCompareAndSet)
            || (m->getRecognizedMethod() == TR::java_util_concurrent_atomic_AtomicReference_weakCompareAndSet))
         {
         casWithoutSync = true;
         }
      }

   generateLabelInstruction(cg, TR::InstOpCode::label, node, startLabel);
   startLabel->setStartInternalControlFlow();

   resultReg = genCAS(node, cg, objReg, offsetReg, oldVReg, newVReg, cndReg, doneLabel, objNode, oldValue, oldValueInReg, dataSize, false, isExchange, casWithoutSync);

   conditions = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(6, 6, cg->trMemory());
   TR::addDependency(conditions, objReg, TR::RealRegister::NoReg, TR_GPR, cg);
   conditions->getPostConditions()->getRegisterDependency(0)->setExcludeGPR0();
   TR::addDependency(conditions, offsetReg, TR::RealRegister::NoReg, TR_GPR, cg);
   TR::addDependency(conditions, resultReg, TR::RealRegister::NoReg, TR_GPR, cg);
   TR::addDependency(conditions, newVReg, TR::RealRegister::NoReg, TR_GPR, cg);
   if (oldValueInReg)
      TR::addDependency(conditions, oldVReg, TR::RealRegister::NoReg, TR_GPR, cg);
   TR::addDependency(conditions, cndReg, TR::RealRegister::cr0, TR_CCR, cg);

   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel, conditions);
   doneLabel->setEndInternalControlFlow();

   cg->stopUsingRegister(cndReg);
   cg->recursivelyDecReferenceCount(firstChild);
   cg->decReferenceCount(objNode);

   if (freeOffsetReg)
      {
      cg->stopUsingRegister(offsetReg);
      cg->recursivelyDecReferenceCount(offsetNode);
      }
   else
      {
      cg->decReferenceCount(offsetNode);
      }

   if (oldValueInReg)
      {
      cg->decReferenceCount(oldVNode);
      }
   else
      {
      cg->recursivelyDecReferenceCount(oldVNode);
      }

   cg->decReferenceCount(newVNode);

   return resultReg;
   }

static TR::Register *VMinlineCompareAndSetOrExchangeReference(TR::Node *node, TR::CodeGenerator *cg, bool isExchange)
   {
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (comp->fe());
   TR::Register *objReg, *offsetReg, *oldVReg, *newVReg, *uncompressedNewVReg, *resultReg, *cndReg;
   TR::Node *firstChild, *objNode, *offsetNode, *oldVNode, *newVNode;
   TR::RegisterDependencyConditions *conditions;
   TR::LabelSymbol *doneLabel, *storeLabel, *wrtBarEndLabel;
   intptr_t offsetValue;
   bool freeOffsetReg;

   auto gcMode = TR::Compiler->om.writeBarrierType();
   bool doWrtBar = (gcMode == gc_modron_wrtbar_satb || gcMode == gc_modron_wrtbar_oldcheck || gcMode == gc_modron_wrtbar_cardmark_and_oldcheck || gcMode == gc_modron_wrtbar_always
         || comp->getOptions()->realTimeGC());
   bool doCrdMrk = (gcMode == gc_modron_wrtbar_cardmark || gcMode == gc_modron_wrtbar_cardmark_and_oldcheck || gcMode == gc_modron_wrtbar_cardmark_incremental);

   firstChild = node->getFirstChild();
   objNode = node->getSecondChild();
   offsetNode = node->getChild(2);
   oldVNode = node->getChild(3);
   newVNode = node->getChild(4);

#if defined(J9VM_GC_SPARSE_HEAP_ALLOCATION)
   bool isObjOffHeapDataAddr = (gcMode == gc_modron_wrtbar_cardmark_incremental && TR::Compiler->om.isOffHeapAllocationEnabled() && objNode->isDataAddrPointer());
   TR::Register *baseObjReg = NULL;
   if (isObjOffHeapDataAddr)
      TR::TreeEvaluator::stopUsingCopyReg(objNode->getFirstChild(), baseObjReg, cg);
#endif /* defined(J9VM_GC_SPARSE_HEAP_ALLOCATION) */
   objReg = cg->evaluate(objNode);

   // VM helper chops off the value in 32bit, and we don't want the whole long value either
   if (offsetNode->getOpCode().isLoadConst() && offsetNode->getRegister() == NULL && comp->target().is32Bit())
      {
      offsetValue = offsetNode->getLongInt();
      offsetReg = cg->allocateRegister();
      loadConstant(cg, node, (int32_t) offsetValue, offsetReg);
      freeOffsetReg = true;
      }
   else
      {
      offsetReg = cg->evaluate(offsetNode);
      freeOffsetReg = false;

      /* Assume that the offset is positive and not pathologically large (i.e., > 2^31). */
      if (comp->target().is32Bit())
         offsetReg = offsetReg->getLowOrder();
      }


   static bool useOldCompareAndSwapObject = (bool)feGetEnv("TR_UseOldCompareAndSwapObject");
   if (!useOldCompareAndSwapObject &&
       comp->target().is64Bit() &&
       (TR::Compiler->om.compressedReferenceShiftOffset() != 0) &&
       ((oldVNode->getOpCodeValue() != TR::aconst) || (oldVNode->getAddress() != 0)))
      {
      oldVReg = cg->gprClobberEvaluate(oldVNode);
      genCompressPointer(cg, node, oldVReg);
      }
   else
      {
      oldVReg = cg->evaluate(oldVNode);
      }

   TR::Node *translatedNode = newVNode;
   bool bumpedRefCount = false;
   if (useOldCompareAndSwapObject && comp->useCompressedPointers() && (newVNode->getDataType() != TR::Address))
      {
      bool useShiftedOffsets = (TR::Compiler->om.compressedReferenceShiftOffset() != 0);

      translatedNode = newVNode;
      if (translatedNode->getOpCode().isConversion())
         translatedNode = translatedNode->getFirstChild();
      if (translatedNode->getOpCode().isRightShift()) // optional
         translatedNode = translatedNode->getFirstChild();

      translatedNode = newVNode;
      if (useShiftedOffsets)
         {
         while ((translatedNode->getNumChildren() > 0) && (translatedNode->getOpCodeValue() != TR::a2l))
            translatedNode = translatedNode->getFirstChild();

         if (translatedNode->getOpCodeValue() == TR::a2l)
            translatedNode = translatedNode->getFirstChild();

         // this is required so that different registers are
         // allocated for the actual store and translated values
         translatedNode->incReferenceCount();
         bumpedRefCount = true;
         }
      }

   if (!useOldCompareAndSwapObject)
      {
      uncompressedNewVReg = cg->evaluate(newVNode);
      if (comp->target().is64Bit() &&
          (TR::Compiler->om.compressedReferenceShiftOffset() != 0) &&
          ((newVNode->getOpCodeValue() != TR::aconst) || (newVNode->getAddress() != 0)))
         {
         newVReg = cg->allocateRegister();
         genCompressPointerWithTempReg(cg, node, newVReg, uncompressedNewVReg);
         }
      else
         {
         newVReg = uncompressedNewVReg;
         }
      }
   else
      {
      newVReg = cg->evaluate(newVNode);
      uncompressedNewVReg = nullptr;
      }

   if (objReg == newVReg)
      {
      newVReg = cg->allocateCollectedReferenceRegister();
      generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, newVReg, objReg);
      }
   cndReg = cg->allocateRegister(TR_CCR);
   doneLabel = generateLabelSymbol(cg);
   storeLabel = generateLabelSymbol(cg);

   if (comp->getOptions()->realTimeGC())
      wrtBarEndLabel = storeLabel;
   else
      wrtBarEndLabel = doneLabel;

#ifdef OMR_GC_CONCURRENT_SCAVENGER
   if (TR::Compiler->om.readBarrierType() != gc_modron_readbar_none)
      {
      TR::Register *tmpReg = cg->allocateRegister();
      TR::Register *locationReg = cg->allocateRegister();
      TR::Register *evacuateReg = cg->allocateRegister();
      TR::Register *r3Reg = cg->allocateRegister();
      TR::Register *r11Reg = cg->allocateRegister();
      TR::Register *metaReg = cg->getMethodMetaDataRegister();

      TR::LabelSymbol *startReadBarrierLabel = generateLabelSymbol(cg);
      TR::LabelSymbol *endReadBarrierLabel = generateLabelSymbol(cg);

      TR::RegisterDependencyConditions *deps = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, 9, cg->trMemory());
      deps->addPostCondition(objReg, TR::RealRegister::NoReg);
      deps->getPostConditions()->getRegisterDependency(0)->setExcludeGPR0();
      deps->addPostCondition(offsetReg, TR::RealRegister::NoReg);
      deps->addPostCondition(tmpReg, TR::RealRegister::NoReg);
      deps->addPostCondition(locationReg, TR::RealRegister::gr4); //TR_softwareReadBarrier helper needs this in gr4.
      deps->addPostCondition(evacuateReg, TR::RealRegister::NoReg);
      deps->addPostCondition(r3Reg, TR::RealRegister::gr3);
      deps->addPostCondition(r11Reg, TR::RealRegister::gr11);
      deps->addPostCondition(metaReg, TR::RealRegister::NoReg);
      deps->addPostCondition(cndReg, TR::RealRegister::NoReg);

      startReadBarrierLabel->setStartInternalControlFlow();
      endReadBarrierLabel->setEndInternalControlFlow();

      TR::InstOpCode::Mnemonic loadOpCode = TR::InstOpCode::lwz;
      TR::InstOpCode::Mnemonic cmpOpCode = TR::InstOpCode::cmpl4;
      int32_t loadWidth = 4;

      if (comp->target().is64Bit() && !comp->useCompressedPointers())
         {
         loadOpCode = TR::InstOpCode::ld;
         cmpOpCode = TR::InstOpCode::cmpl8;
         loadWidth = 8;
         }

      generateTrg1MemInstruction(cg, loadOpCode, node, tmpReg, TR::MemoryReference::createWithIndexReg(cg, objReg, offsetReg, loadWidth));

      generateLabelInstruction(cg, TR::InstOpCode::label, node, startReadBarrierLabel);

      generateTrg1MemInstruction(cg, loadOpCode, node, evacuateReg,
            TR::MemoryReference::createWithDisplacement(cg, metaReg, comp->fej9()->thisThreadGetEvacuateBaseAddressOffset(), loadWidth));
      generateTrg1Src2Instruction(cg, cmpOpCode, node, cndReg, tmpReg, evacuateReg);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, endReadBarrierLabel, cndReg);

      generateTrg1MemInstruction(cg, loadOpCode, node, evacuateReg,
            TR::MemoryReference::createWithDisplacement(cg, metaReg, comp->fej9()->thisThreadGetEvacuateTopAddressOffset(), loadWidth));
      generateTrg1Src2Instruction(cg, cmpOpCode, node, cndReg, tmpReg, evacuateReg);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bgt, node, endReadBarrierLabel, cndReg);

      generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, locationReg, objReg, offsetReg);

      // TR_softwareReadBarrier helper expects the vmThread in r3.
      generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, r3Reg, metaReg);

      TR::SymbolReference *helperSym = comp->getSymRefTab()->findOrCreateRuntimeHelper(TR_softwareReadBarrier);
      generateDepImmSymInstruction(cg, TR::InstOpCode::bl, node, (uintptr_t)helperSym->getMethodAddress(), deps, helperSym);

      generateDepLabelInstruction(cg, TR::InstOpCode::label, node, endReadBarrierLabel, deps);

      cg->stopUsingRegister(tmpReg);
      cg->stopUsingRegister(locationReg);
      cg->stopUsingRegister(evacuateReg);
      cg->stopUsingRegister(r11Reg);
      cg->stopUsingRegister(r3Reg);

      cg->machine()->setLinkRegisterKilled(true);
      }
#endif //OMR_GC_CONCURRENT_SCAVENGER

   if (!comp->getOptions()->realTimeGC())
      resultReg = genCAS(node, cg, objReg, offsetReg, oldVReg, newVReg, cndReg, doneLabel, objNode, 0, true, TR::Compiler->om.sizeofReferenceField(), true, isExchange, false);

   uint32_t numDeps = (doWrtBar || doCrdMrk) ? 13 : 11;

   if (doWrtBar) //two extra deps for space boundaries
      numDeps += 2;

   conditions = createConditionsAndPopulateVSXDeps(cg, numDeps);

   if (doWrtBar && doCrdMrk)
      {
      TR::Register *temp1Reg = cg->allocateRegister(), *temp2Reg = cg->allocateRegister(), *temp3Reg, *temp4Reg = cg->allocateRegister();
      TR::addDependency(conditions, objReg, TR::RealRegister::gr3, TR_GPR, cg);
      TR::Register *wrtbarSrcReg;

      if (!useOldCompareAndSwapObject)
         {
         if (uncompressedNewVReg != newVReg)
            {
            TR::addDependency(conditions, newVReg, TR::RealRegister::NoReg, TR_GPR, cg);
            }
         TR::addDependency(conditions, uncompressedNewVReg, TR::RealRegister::gr4, TR_GPR, cg);
         wrtbarSrcReg = uncompressedNewVReg;
         }
      else
         {
         if (translatedNode != newVNode)
            {
            TR::addDependency(conditions, newVReg, TR::RealRegister::NoReg, TR_GPR, cg);
            TR::addDependency(conditions, translatedNode->getRegister(), TR::RealRegister::gr4, TR_GPR, cg);
            wrtbarSrcReg = translatedNode->getRegister();
            }
         else
            {
            TR::addDependency(conditions, newVReg, TR::RealRegister::gr4, TR_GPR, cg);
            wrtbarSrcReg = newVReg;
            }
         }

      TR::addDependency(conditions, temp1Reg, TR::RealRegister::gr11, TR_GPR, cg);
      TR::addDependency(conditions, temp2Reg, TR::RealRegister::NoReg, TR_GPR, cg);
      conditions->getPostConditions()->getRegisterDependency(conditions->getAddCursorForPost() - 1)->setExcludeGPR0();
      TR::addDependency(conditions, offsetReg, TR::RealRegister::NoReg, TR_GPR, cg);
      TR::addDependency(conditions, temp4Reg, TR::RealRegister::NoReg, TR_GPR, cg);

      if (freeOffsetReg)
         {
         temp3Reg = offsetReg;
         }
      else
         {
         temp3Reg = cg->allocateRegister();
         TR::addDependency(conditions, temp3Reg, TR::RealRegister::NoReg, TR_GPR, cg);
         }

      if (!newVNode->isNonNull())
         {
         generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpi, node, cndReg, newVReg, NULLVALUE);
         generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, cndReg);
         }

      VMnonNullSrcWrtBarCardCheckEvaluator(node, comp->useCompressedPointers() ? wrtbarSrcReg : newVReg, objReg, cndReg, temp1Reg, temp2Reg, temp3Reg, temp4Reg, doneLabel,
            conditions, comp->useCompressedPointers(), cg);

      cg->stopUsingRegister(temp1Reg);
      cg->stopUsingRegister(temp2Reg);
      if (!freeOffsetReg)
         cg->stopUsingRegister(temp3Reg);
      cg->stopUsingRegister(temp4Reg);
      }
   else if (doWrtBar && !doCrdMrk)
      {
      TR::Register *temp1Reg = cg->allocateRegister(), *temp2Reg;
      TR::addDependency(conditions, objReg, TR::RealRegister::gr3, TR_GPR, cg);
      TR::Register *wrtbarSrcReg;

      if (!useOldCompareAndSwapObject)
         {
         if (newVReg != uncompressedNewVReg)
            {
            TR::addDependency(conditions, newVReg, TR::RealRegister::NoReg, TR_GPR, cg);
            }

         if (comp->getOptions()->realTimeGC())
            {
            TR::addDependency(conditions, uncompressedNewVReg, TR::RealRegister::gr5, TR_GPR, cg);
            }
         else
            {
            TR::addDependency(conditions, uncompressedNewVReg, TR::RealRegister::gr4, TR_GPR, cg);
            }

         wrtbarSrcReg = uncompressedNewVReg;
         }
      else
         {
         if (newVReg != translatedNode->getRegister())
            {
            TR::addDependency(conditions, newVReg, TR::RealRegister::NoReg, TR_GPR, cg);
            if (comp->getOptions()->realTimeGC())
               TR::addDependency(conditions, translatedNode->getRegister(), TR::RealRegister::gr5, TR_GPR, cg);
            else
               TR::addDependency(conditions, translatedNode->getRegister(), TR::RealRegister::gr4, TR_GPR, cg);
            }
         else
            {
            if (comp->getOptions()->realTimeGC())
               TR::addDependency(conditions, newVReg, TR::RealRegister::gr5, TR_GPR, cg);
            else
               TR::addDependency(conditions, newVReg, TR::RealRegister::gr4, TR_GPR, cg);
            }
         wrtbarSrcReg = translatedNode->getRegister();
         }

      TR::addDependency(conditions, temp1Reg, TR::RealRegister::NoReg, TR_GPR, cg);

      //Realtime needs the offsetReg to be preserved after the wrtbar to do the store in genCAS()
      if (freeOffsetReg && !comp->getOptions()->realTimeGC())
         {
         TR::addDependency(conditions, offsetReg, TR::RealRegister::gr11, TR_GPR, cg);
         temp2Reg = offsetReg;
         }
      else
         {
         temp2Reg = cg->allocateRegister();
         TR::addDependency(conditions, temp2Reg, TR::RealRegister::gr11, TR_GPR, cg);
         TR::addDependency(conditions, offsetReg, TR::RealRegister::NoReg, TR_GPR, cg);
         }

      if (!newVNode->isNonNull())
         {
         generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpi, node, cndReg, newVReg, NULLVALUE);
         generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, wrtBarEndLabel, cndReg);
         }

      TR::Register *dstAddrReg = NULL;

      if (comp->getOptions()->realTimeGC())
         {
         dstAddrReg = cg->allocateRegister();
         TR::addDependency(conditions, dstAddrReg, TR::RealRegister::gr4, TR_GPR, cg);
         generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, dstAddrReg, objReg, offsetReg);
         }

      VMnonNullSrcWrtBarCardCheckEvaluator(node, comp->useCompressedPointers() ? wrtbarSrcReg : newVReg, objReg, cndReg, temp1Reg, temp2Reg, dstAddrReg, NULL,
            wrtBarEndLabel, conditions, comp->useCompressedPointers(), cg);

      if (comp->getOptions()->realTimeGC())
         cg->stopUsingRegister(dstAddrReg);

      cg->stopUsingRegister(temp1Reg);
      if (!freeOffsetReg || comp->getOptions()->realTimeGC())
         cg->stopUsingRegister(temp2Reg);
      }
   else if (!doWrtBar && doCrdMrk)
      {
      TR::Register *temp1Reg = cg->allocateRegister(), *temp2Reg, *temp3Reg;
#if defined(J9VM_GC_SPARSE_HEAP_ALLOCATION)
      if (isObjOffHeapDataAddr)
         temp2Reg = baseObjReg; // Use baseObjReg as temp2Reg
      else
#endif /* defined(J9VM_GC_SPARSE_HEAP_ALLOCATION) */
         temp2Reg = cg->allocateRegister();
      TR::addDependency(conditions, objReg, TR::RealRegister::NoReg, TR_GPR, cg);
      conditions->getPostConditions()->getRegisterDependency(0)->setExcludeGPR0();
      TR::addDependency(conditions, temp1Reg, TR::RealRegister::NoReg, TR_GPR, cg);
      conditions->getPostConditions()->getRegisterDependency(1)->setExcludeGPR0();

      if (!useOldCompareAndSwapObject)
         {
         if (newVReg != uncompressedNewVReg)
            {
            TR::addDependency(conditions, newVReg, TR::RealRegister::NoReg, TR_GPR, cg);
            }
         TR::addDependency(conditions, uncompressedNewVReg, TR::RealRegister::NoReg, TR_GPR, cg);
         }
      else
         {
         if (newVReg != translatedNode->getRegister())
            TR::addDependency(conditions, newVReg, TR::RealRegister::NoReg, TR_GPR, cg);
         TR::addDependency(conditions, translatedNode->getRegister(), TR::RealRegister::NoReg, TR_GPR, cg);
         }

      TR::addDependency(conditions, temp2Reg, TR::RealRegister::NoReg, TR_GPR, cg);
      TR::addDependency(conditions, offsetReg, TR::RealRegister::NoReg, TR_GPR, cg);
      if (freeOffsetReg)
         {
         temp3Reg = offsetReg;
         }
      else
         {
         temp3Reg = cg->allocateRegister();
         TR::addDependency(conditions, temp3Reg, TR::RealRegister::NoReg, TR_GPR, cg);
         }

#if defined(J9VM_GC_SPARSE_HEAP_ALLOCATION)
      if (isObjOffHeapDataAddr)
         // Given that baseObjReg is trash-able it can be used as the objReg and temp2Reg
         VMCardCheckEvaluator(node, baseObjReg, cndReg, temp1Reg, temp2Reg, temp3Reg, conditions, cg);
      else
#endif /* defined(J9VM_GC_SPARSE_HEAP_ALLOCATION) */
         VMCardCheckEvaluator(node, objReg, cndReg, temp1Reg, temp2Reg, temp3Reg, conditions, cg);

      cg->stopUsingRegister(temp1Reg);
      cg->stopUsingRegister(temp2Reg);
      if (!freeOffsetReg)
         cg->stopUsingRegister(temp3Reg);
      }
   else
      {
      TR::addDependency(conditions, objReg, TR::RealRegister::NoReg, TR_GPR, cg);
      conditions->getPostConditions()->getRegisterDependency(0)->setExcludeGPR0();
      TR::addDependency(conditions, offsetReg, TR::RealRegister::NoReg, TR_GPR, cg);
      if (!useOldCompareAndSwapObject)
         {
         if (newVReg != uncompressedNewVReg)
            TR::addDependency(conditions, newVReg, TR::RealRegister::NoReg, TR_GPR, cg);
         TR::addDependency(conditions, uncompressedNewVReg, TR::RealRegister::NoReg, TR_GPR, cg);
         }
      else
         {
         if (newVReg != translatedNode->getRegister())
            TR::addDependency(conditions, newVReg, TR::RealRegister::NoReg, TR_GPR, cg);
         TR::addDependency(conditions, translatedNode->getRegister(), TR::RealRegister::NoReg, TR_GPR, cg);
         }
      }

   generateLabelInstruction(cg, TR::InstOpCode::label, node, storeLabel);

   if (comp->getOptions()->realTimeGC())
      resultReg = genCAS(node, cg, objReg, offsetReg, oldVReg, newVReg, cndReg, doneLabel, objNode, 0, true, TR::Compiler->om.sizeofReferenceField(), true, isExchange, false);

   TR::addDependency(conditions, resultReg, TR::RealRegister::NoReg, TR_GPR, cg);
   if (oldVReg != newVReg && oldVReg != objReg)
      TR::addDependency(conditions, oldVReg, TR::RealRegister::NoReg, TR_GPR, cg);
   TR::addDependency(conditions, cndReg, TR::RealRegister::cr0, TR_CCR, cg);

   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel, conditions);

   if (!useOldCompareAndSwapObject)
      {
      cg->stopUsingRegister(oldVReg);
      }

   cg->stopUsingRegister(newVReg);
   cg->stopUsingRegister(cndReg);

   cg->recursivelyDecReferenceCount(firstChild);
   cg->decReferenceCount(objNode);

   if (freeOffsetReg)
      {
      cg->stopUsingRegister(offsetReg);
      cg->recursivelyDecReferenceCount(offsetNode);
      }
   else
      {
      cg->decReferenceCount(offsetNode);
      }

   cg->decReferenceCount(oldVNode);
   cg->decReferenceCount(newVNode);

   if (bumpedRefCount)
      cg->decReferenceCount(translatedNode);

   return resultReg;
   }


void J9::Power::TreeEvaluator::restoreTOCRegister(TR::Node *node, TR::CodeGenerator *cg, TR::RegisterDependencyConditions *dependencies)
   {
#if defined(TR_HOST_POWER)
   TR::Compilation *comp = cg->comp();
   TR::Register *tocReg = dependencies->searchPreConditionRegister(TR::RealRegister::gr2);
   generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, tocReg,
         TR::MemoryReference::createWithDisplacement(cg, cg->getMethodMetaDataRegister(), offsetof(J9VMThread, jitTOC), TR::Compiler->om.sizeofReferenceAddress()));
#else
   TR_ASSERT(0, "direct C calls directly are not supported on this platform.\n");
#endif
   }

void J9::Power::TreeEvaluator::buildArgsProcessFEDependencies(TR::Node *node, TR::CodeGenerator *cg, TR::RegisterDependencyConditions *dependencies)
   {
   TR::Compilation *comp = cg->comp();
   //Java uses gr2 as a volatile for targets except for 32-bit Linux.
   bool aix_style_linkage = (comp->target().isAIX() || (comp->target().is64Bit() && comp->target().isLinux()));

   if(aix_style_linkage)
      TR::addDependency(dependencies, NULL, TR::RealRegister::gr2, TR_GPR, cg);
   }

TR::Register *J9::Power::TreeEvaluator::retrieveTOCRegister(TR::Node *node, TR::CodeGenerator *cg, TR::RegisterDependencyConditions *dependencies)
   {
#if defined(DEBUG)
   TR::Compilation *comp = cg->comp();
   //We should not land here if we're compiling for 32-bit Linux.
   bool aix_style_linkage = (comp->target().isAIX() || (comp->target().is64Bit() && comp->target().isLinux()));
   TR_ASSERT(aix_style_linkage, "Landed to restore gr2 for TOC with 32bit Linux as target\n");
#endif

   TR::Register *grTOCReg=dependencies->searchPreConditionRegister(TR::RealRegister::gr2);
   TR_ASSERT(grTOCReg != NULL, "Dependency not set in J9 Java on gr2 for TOC.\n");
   return grTOCReg;
   }


static TR::Register *inlineAtomicOps(TR::Node *node, TR::CodeGenerator *cg, int8_t size, TR::MethodSymbol *method, bool isArray)
   {
   TR::Node *valueChild = node->getFirstChild();
   TR::Node *deltaChild = NULL;
   TR::Register *valueReg = cg->evaluate(valueChild);
   TR::Register *deltaReg = NULL;
   TR::Register *resultReg = cg->allocateRegister();
   TR::Register *cndReg = cg->allocateRegister(TR_CCR);
   TR::Register *fieldOffsetReg = cg->allocateRegister();
   TR::Register *tempReg = NULL;
   int32_t delta = 0;
   int32_t numDeps = 5;
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *)(comp->fe());

   bool isAddOp = true;
   bool isGetAndOp = true;
   bool isLong = false;
   bool isArgConstant = false;
   bool isArgImmediate = false;
   bool isArgImmediateShifted = false;
   TR::RecognizedMethod currentMethod = method->getRecognizedMethod();

   switch (currentMethod)
      {
      case TR::java_util_concurrent_atomic_AtomicBoolean_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicInteger_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicLong_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicReference_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicLongArray_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicReferenceArray_getAndSet:
         {
         isAddOp = false;
         break;
         }
      case TR::java_util_concurrent_atomic_AtomicInteger_addAndGet:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_addAndGet:
         {
         isGetAndOp = false;
         }
      case TR::java_util_concurrent_atomic_AtomicInteger_getAndAdd:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndAdd:
         {
         break;
         }

      case TR::java_util_concurrent_atomic_AtomicInteger_incrementAndGet:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_incrementAndGet:
         {
         isGetAndOp = false;
         }
      case TR::java_util_concurrent_atomic_AtomicInteger_getAndIncrement:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndIncrement:
         {
         delta = (int32_t) 1;
         isArgConstant = true;
         isArgImmediate = true;
         break;
         }
      case TR::java_util_concurrent_atomic_AtomicInteger_decrementAndGet:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_decrementAndGet:
         {
         isGetAndOp = false;
         }
      case TR::java_util_concurrent_atomic_AtomicInteger_getAndDecrement:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndDecrement:
         {
         delta = (int32_t) - 1;
         isArgConstant = true;
         isArgImmediate = true;
         break;
         }
      default:
         break;
      }

   if (node->getNumChildren() > 1)
      deltaChild = node->getSecondChild();

   //determine if the delta is a constant.
   if (deltaChild && deltaChild->getOpCode().isLoadConst() && !deltaChild->getRegister() && deltaChild->getDataType() == TR::Int32)
      {
      delta = (int32_t)(deltaChild->getInt());
      isArgConstant = true;

      //determine if the constant can be represented as an immediate
      if (delta <= UPPER_IMMED && delta >= LOWER_IMMED)
         {
         // avoid evaluating immediates for add operations
         isArgImmediate = true;
         }
      else if (delta & 0xFFFF == 0 && (delta & 0xFFFF0000) >> 16 <= UPPER_IMMED && (delta & 0xFFFF0000) >> 16 >= LOWER_IMMED)
         {
         // avoid evaluating shifted immediates for add operations
         isArgImmediate = true;
         isArgImmediateShifted = true;
         }
      else
         {
         // evaluate non-immediate constants since there may be reuse
         // and they have to go into a reg anyway
         tempReg = cg->evaluate(deltaChild);
         }
      }
   else if (deltaChild)
      tempReg = cg->evaluate(deltaChild);

   //determine the offset of the value field
   int32_t fieldOffset = 0;
   int32_t shiftAmount = 0;
   TR::Node *indexChild = NULL;
   TR::Register *indexRegister = NULL;

   TR::Register *scratchRegister = NULL;

   if (!isArray)
      {
      TR_OpaqueClassBlock * bdClass;
      const char *className, *fieldSig;
      int32_t classNameLen, fieldSigLen;

      fieldSigLen = 1;

      switch (currentMethod)
         {
         case TR::java_util_concurrent_atomic_AtomicBoolean_getAndSet:
            className = "Ljava/util/concurrent/atomic/AtomicBoolean;";
            classNameLen = 43;
            fieldSig = "I"; // not a typo, the field is int
            break;
         case TR::java_util_concurrent_atomic_AtomicInteger_getAndSet:
         case TR::java_util_concurrent_atomic_AtomicInteger_addAndGet:
         case TR::java_util_concurrent_atomic_AtomicInteger_getAndAdd:
         case TR::java_util_concurrent_atomic_AtomicInteger_incrementAndGet:
         case TR::java_util_concurrent_atomic_AtomicInteger_getAndIncrement:
         case TR::java_util_concurrent_atomic_AtomicInteger_decrementAndGet:
         case TR::java_util_concurrent_atomic_AtomicInteger_getAndDecrement:
            className = "Ljava/util/concurrent/atomic/AtomicInteger;";
            classNameLen = 43;
            fieldSig = "I";
            break;
         case TR::java_util_concurrent_atomic_AtomicLong_getAndSet:
         case TR::java_util_concurrent_atomic_AtomicLong_addAndGet:
         case TR::java_util_concurrent_atomic_AtomicLong_getAndAdd:
         case TR::java_util_concurrent_atomic_AtomicLong_incrementAndGet:
         case TR::java_util_concurrent_atomic_AtomicLong_getAndIncrement:
         case TR::java_util_concurrent_atomic_AtomicLong_decrementAndGet:
         case TR::java_util_concurrent_atomic_AtomicLong_getAndDecrement:
            className = "Ljava/util/concurrent/atomic/AtomicLong;";
            classNameLen = 40;
            fieldSig = "J";
            break;
         case TR::java_util_concurrent_atomic_AtomicReference_getAndSet:
            className = "Ljava/util/concurrent/atomic/AtomicReference;";
            classNameLen = 45;
            fieldSig = "Ljava/lang/Object;";
            fieldSigLen = 18;
            break;
         default:
            TR_ASSERT(0, "Unknown atomic operation method\n");
            return NULL;
         }

      TR_ResolvedMethod *owningMethod = node->getSymbolReference()->getOwningMethod(comp);
      TR_OpaqueClassBlock *containingClass = fej9->getClassFromSignature(className, classNameLen, owningMethod, true);
      fieldOffset = fej9->getInstanceFieldOffset(containingClass, "value", 5, fieldSig, fieldSigLen, true) + fej9->getObjectHeaderSizeInBytes(); // size of a J9 object header
      }
   else
      {
      if (isArray)
         {
         indexChild = node->getChild(1);
         indexRegister = cg->evaluate(indexChild);
         fieldOffset = TR::Compiler->om.contiguousArrayHeaderSizeInBytes();
         if (size == 4)
            shiftAmount = 2;
         else if (size == 8)
            shiftAmount = 3;

         TR_OpaqueClassBlock * bdClass;
         const char *className, *fieldSig;
         int32_t classNameLen, fieldSigLen;

         fieldSigLen = 1;

         switch (currentMethod)
            {
            case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndSet:
            case TR::java_util_concurrent_atomic_AtomicIntegerArray_incrementAndGet:
            case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndIncrement:
            case TR::java_util_concurrent_atomic_AtomicIntegerArray_decrementAndGet:
            case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndDecrement:
            case TR::java_util_concurrent_atomic_AtomicIntegerArray_addAndGet:
            case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndAdd:
               className = "Ljava/util/concurrent/atomic/AtomicIntegerArray;";
               classNameLen = 48;
               fieldSig = "[I";
               fieldSigLen = 2;
               break;

            case TR::java_util_concurrent_atomic_AtomicLongArray_getAndSet:
            case TR::java_util_concurrent_atomic_AtomicLongArray_incrementAndGet:
            case TR::java_util_concurrent_atomic_AtomicLongArray_getAndIncrement:
            case TR::java_util_concurrent_atomic_AtomicLongArray_decrementAndGet:
            case TR::java_util_concurrent_atomic_AtomicLongArray_getAndDecrement:
            case TR::java_util_concurrent_atomic_AtomicLongArray_addAndGet:
            case TR::java_util_concurrent_atomic_AtomicLongArray_getAndAdd:
               className = "Ljava/util/concurrent/atomic/AtomicLongArray;";
               classNameLen = 45;
               fieldSig = "[J";
               fieldSigLen = 2;
               break;

            case TR::java_util_concurrent_atomic_AtomicReferenceArray_getAndSet:
               className = "Ljava/util/concurrent/atomic/AtomicReferenceArray;";
               classNameLen = 50;
               fieldSig = "Ljava/lang/Object;";
               fieldSigLen = 18;
               break;
            default:
               break;
            }

         TR_ResolvedMethod *owningMethod = node->getSymbolReference()->getOwningMethod(comp);
         TR_OpaqueClassBlock *containingClass = fej9->getClassFromSignature(className, classNameLen, owningMethod, true);
         int32_t arrayFieldOffset = fej9->getInstanceFieldOffset(containingClass, "array", 5, fieldSig, fieldSigLen) + fej9->getObjectHeaderSizeInBytes(); // size of a J9 object header

         TR::MemoryReference *tempMR = TR::MemoryReference::createWithDisplacement(cg, valueReg, arrayFieldOffset, isLong ? 8 : 4);

         numDeps++;
         scratchRegister = cg->allocateCollectedReferenceRegister();
         TR::Register *memRefRegister = scratchRegister;

         if (TR::Compiler->om.compressObjectReferences())
            // read only 32 bits
            generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, memRefRegister,
                  TR::MemoryReference::createWithDisplacement(cg, valueReg, arrayFieldOffset, 4));
         else
            generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, memRefRegister, TR::MemoryReference::createWithDisplacement(cg, valueReg, arrayFieldOffset, TR::Compiler->om.sizeofReferenceAddress()));

         valueReg = memRefRegister;

         generateShiftLeftImmediate(cg, node, fieldOffsetReg, indexRegister, shiftAmount);
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi2, node, fieldOffsetReg, fieldOffsetReg, fieldOffset);
         }
      }

   // Memory barrier --- NOTE: we should be able to do a test upfront to save this barrier,
   //                          but Hursley advised to be conservative due to lack of specification.
   generateInstruction(cg, TR::InstOpCode::lwsync, node);

   TR::LabelSymbol *doneLabel = TR::LabelSymbol::create(cg->trHeapMemory(),cg);
   TR::LabelSymbol *loopLabel = TR::LabelSymbol::create(cg->trHeapMemory(),cg);

   loopLabel->setStartInternalControlFlow();
   if (!isArray)
      generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, fieldOffsetReg, fieldOffset);

   deltaReg = cg->allocateRegister();
   if (isArgImmediate && isAddOp)
      {
      // If argument is an immediate value and operation is an add,
      // it will be used as an immediate operand in an add immediate instruction
      generateLabelInstruction(cg, TR::InstOpCode::label, node, loopLabel);
      }
   else if (isArgImmediate)
      {
      // If argument is immediate, but the operation is not an add,
      // the value must still be loaded into a register
      generateLabelInstruction(cg, TR::InstOpCode::label, node, loopLabel);
      loadConstant(cg, node, delta, deltaReg);
      }
   else
      {
      // For non-constant arguments, use evaluated register
      // For non-immediate constants, evaluate since they may be re-used
      numDeps++;
      generateLabelInstruction(cg, TR::InstOpCode::label, node, loopLabel);
      generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, deltaReg, tempReg);
      }

   generateTrg1MemInstruction(cg, isLong ? TR::InstOpCode::ldarx : TR::InstOpCode::lwarx, node, resultReg,
         TR::MemoryReference::createWithIndexReg(cg, valueReg, fieldOffsetReg, isLong ? 8 : 4));

   if (isAddOp)
      {
      if (isArgImmediateShifted)
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addis, node, deltaReg, resultReg, ((delta & 0xFFFF0000) >> 16));
      else if (isArgImmediate)
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, deltaReg, resultReg, delta);
      else
         generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, deltaReg, resultReg, deltaReg);
      }

   generateMemSrc1Instruction(cg, isLong ? TR::InstOpCode::stdcx_r : TR::InstOpCode::stwcx_r, node, TR::MemoryReference::createWithIndexReg(cg, valueReg, fieldOffsetReg, isLong ? 8 : 4),
         deltaReg);

   // We expect this store is usually successful, i.e., the following branch will not be taken
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, PPCOpProp_BranchUnlikely, node, loopLabel, cndReg);

   // We deviate from the VM helper here: no-store-no-barrier instead of always-barrier
   generateInstruction(cg, TR::InstOpCode::sync, node);

   TR::RegisterDependencyConditions *conditions;

   //Set the conditions and dependencies
   conditions = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, (uint16_t) numDeps, cg->trMemory());
   conditions->addPostCondition(valueReg, TR::RealRegister::NoReg);
   conditions->getPostConditions()->getRegisterDependency(0)->setExcludeGPR0();
   conditions->addPostCondition(resultReg, TR::RealRegister::NoReg);
   conditions->getPostConditions()->getRegisterDependency(1)->setExcludeGPR0();
   conditions->addPostCondition(deltaReg, TR::RealRegister::NoReg);
   conditions->addPostCondition(cndReg, TR::RealRegister::cr0);
   conditions->addPostCondition(fieldOffsetReg, TR::RealRegister::NoReg);
   if (tempReg)
      conditions->addPostCondition(tempReg, TR::RealRegister::NoReg);
   if (scratchRegister)
      conditions->addPostCondition(scratchRegister, TR::RealRegister::NoReg);

   doneLabel->setEndInternalControlFlow();
   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel, conditions);

   cg->decReferenceCount(valueChild);
   cg->stopUsingRegister(cndReg);
   cg->stopUsingRegister(fieldOffsetReg);

   if (tempReg)
      cg->stopUsingRegister(tempReg);

   if (scratchRegister)
      cg->stopUsingRegister(scratchRegister);

   if (deltaChild)
      cg->decReferenceCount(deltaChild);

   if (isGetAndOp)
      {
      //for Get And Op, we will store the result in the result register
      cg->stopUsingRegister(deltaReg);
      node->setRegister(resultReg);
      return resultReg;
      }
   else
      {
      //for Op And Get, we will store the return value in the delta register
      //we no longer need the result register
      cg->stopUsingRegister(resultReg);
      node->setRegister(deltaReg);
      return deltaReg;
      }
   }

static TR::Register *inlineSinglePrecisionFP(TR::Node *node, TR::InstOpCode::Mnemonic op, TR::CodeGenerator *cg)
   {
   TR_ASSERT(node->getNumChildren() == 1, "Wrong number of children in inlineSinglePrecisionFP");

   TR::Node *firstChild = node->getFirstChild();
   TR::Register *srcRegister = cg->evaluate(firstChild);
   TR::Register *targetRegister = cg->allocateSinglePrecisionRegister();

   generateTrg1Src1Instruction(cg, op, node, targetRegister, srcRegister);

   node->setRegister(targetRegister);
   cg->decReferenceCount(firstChild);

   return targetRegister;
   }

static TR::Register *inlineSinglePrecisionFPTrg1Src2(TR::Node *node, TR::InstOpCode::Mnemonic op, TR::CodeGenerator *cg)
   {
   TR_ASSERT(node->getNumChildren() == 2, "Wrong number of children in inlineSinglePrecisionFPTrg1Src2");

   TR::Node *firstChild = node->getFirstChild();
   TR::Node *secondChild = node->getSecondChild();
   TR::Register *src1Register = cg->evaluate(firstChild);
   TR::Register *src2Register = cg->evaluate(secondChild);
   TR::Register *targetRegister = cg->allocateSinglePrecisionRegister();

   if (op == TR::InstOpCode::fcpsgn) // fcpsgn orders operands opposite of Math.copySign
      generateTrg1Src2Instruction(cg, op, node, targetRegister, src2Register, src1Register);
   else
      generateTrg1Src2Instruction(cg, op, node, targetRegister, src1Register, src2Register);

   node->setRegister(targetRegister);
   cg->decReferenceCount(firstChild);
   cg->decReferenceCount(secondChild);
   return targetRegister;
   }

static TR::Register *inlineFPTrg1Src3(TR::Node *node, TR::InstOpCode::Mnemonic op, TR::CodeGenerator *cg)
   {
   TR_ASSERT_FATAL(node->getNumChildren() == 3, "In function inlineFPTrg1Src3, the node at address %p should have exactly 3 children, but got %u instead", node, node->getNumChildren());

   TR::DataType type = node->getDataType();
   TR_ASSERT_FATAL(type == TR::Float || type == TR::Double, "In function inlineFPTrg1Src3, the node at address %p should be either TR::Float or TR::Double", node);

   TR::Node *firstChild = node->getFirstChild();
   TR::Node *secondChild = node->getSecondChild();
   TR::Node *thirdChild = node->getThirdChild();
   TR::Register *src1Register = cg->evaluate(firstChild);
   TR::Register *src2Register = cg->evaluate(secondChild);
   TR::Register *src3Register = cg->evaluate(thirdChild);
   TR::Register *targetRegister;

   if(type == TR::Float)
      targetRegister = cg->allocateSinglePrecisionRegister();
   else
      targetRegister = cg->allocateRegister(TR_FPR);

   generateTrg1Src3Instruction(cg, op, node, targetRegister, src1Register, src2Register, src3Register);

   node->setRegister(targetRegister);
   cg->decReferenceCount(firstChild);
   cg->decReferenceCount(secondChild);
   cg->decReferenceCount(thirdChild);
   return targetRegister;
   }

static TR::Register *inlineDoublePrecisionFP(TR::Node *node, TR::InstOpCode::Mnemonic op, TR::CodeGenerator *cg)
   {
   TR_ASSERT(node->getNumChildren() == 1, "Wrong number of children in inlineDoublePrecisionFP");

   TR::Node *firstChild = node->getFirstChild();
   TR::Register *srcRegister = cg->evaluate(firstChild);
   TR::Register *targetRegister = cg->allocateRegister(TR_FPR);

   generateTrg1Src1Instruction(cg, op, node, targetRegister, srcRegister);

   node->setRegister(targetRegister);
   cg->decReferenceCount(firstChild);

   return targetRegister;
   }

static TR::Register *inlineDoublePrecisionFPTrg1Src2(TR::Node *node, TR::InstOpCode::Mnemonic op, TR::CodeGenerator *cg)
   {
   TR_ASSERT(node->getNumChildren() == 2, "Wrong number of children in inlineDoublePrecisionFPTrg1Src2");

   TR::Node *firstChild = node->getFirstChild();
   TR::Node *secondChild = node->getSecondChild();
   TR::Register *src1Register = cg->evaluate(firstChild);
   TR::Register *src2Register = cg->evaluate(secondChild);
   TR::Register *targetRegister = cg->allocateRegister(TR_FPR);

   if (op == TR::InstOpCode::fcpsgn) // fcpsgn orders operands opposite of Math.copySign
      generateTrg1Src2Instruction(cg, op, node, targetRegister, src2Register, src1Register);
   else
      generateTrg1Src2Instruction(cg, op, node, targetRegister, src1Register, src2Register);

   node->setRegister(targetRegister);
   cg->decReferenceCount(firstChild);
   cg->decReferenceCount(secondChild);
   return targetRegister;
   }

static TR::Register *inlineAtomicOperation(TR::Node *node, TR::CodeGenerator *cg, TR::MethodSymbol *method)
   {
   TR::Node *firstChild = NULL;
   TR::Node *valueChild = NULL;
   TR::Node *indexChild = NULL;
   TR::Node *deltaChild = NULL;
   TR::Node *newValChild = NULL;
   TR::Node *expValChild = NULL;
   TR::Node *newRefChild = NULL;
   TR::Node *expRefChild = NULL;
   TR::Node *overNewNode = NULL;
   TR::Node *overExpNode = NULL;

   TR::LabelSymbol *startLabel = NULL;
   TR::LabelSymbol *failLabel = NULL;
   TR::LabelSymbol *doneLabel = NULL;

   TR::Register *valueReg = NULL;
   TR::Register *scratchReg = NULL;
   TR::Register *fieldOffsetReg = NULL;
   TR::Register *cndReg = NULL;
   TR::Register *currentReg = NULL;
   TR::Register *newComposedReg = NULL;
   TR::Register *expComposedReg = NULL;
   TR::Register *expRefReg = NULL;
   TR::Register *newRefReg = NULL;
   TR::Register *expValReg = NULL;
   TR::Register *newValReg = NULL;
   TR::Register *resultReg = NULL;
   TR::Register *objReg = NULL;
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *)(comp->fe());

   bool isArray = false;
   bool isAddOp = true;
   bool isDeltaImplied = false;
   bool isDelta = false;
   bool isGetAndOp = false;
   bool isCAS = false;
   bool isSetOnly = false;
   bool isWeak = false;
   bool isRefFirst = false;
   bool isUnsafe = false;

   bool isArgImm = false;
   bool isArgImmShifted = false;

   bool fieldOffsetRegIsEval = false;
   bool isRefWrite = false;

   bool newPair = false;
   bool expPair = false;

   bool stopNewComposedCopy = false;

   int64_t expValImm;
   int32_t delta;
   uint8_t size = 4;
   int32_t fieldOffset = 0;
   int32_t idx;
   uint8_t numDeps = 1;

   TR::RecognizedMethod currentMethod = method->getRecognizedMethod();

   switch (currentMethod)
      {
      case TR::sun_misc_Unsafe_compareAndSwapLong_jlObjectJJJ_Z:
         size = 8;
         isUnsafe = true;
         isCAS = true;
         isAddOp = false;
         break;
      case TR::sun_misc_Unsafe_compareAndSwapObject_jlObjectJjlObjectjlObject_Z:
         size = TR::Compiler->om.sizeofReferenceAddress();
         isRefWrite = true;
      case TR::sun_misc_Unsafe_compareAndSwapInt_jlObjectJII_Z:
         isUnsafe = true;
         isCAS = true;
         isAddOp = false;
         break;
      case TR::java_util_concurrent_atomic_AtomicBoolean_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicInteger_addAndGet:
      case TR::java_util_concurrent_atomic_AtomicInteger_decrementAndGet:
      case TR::java_util_concurrent_atomic_AtomicInteger_getAndAdd:
      case TR::java_util_concurrent_atomic_AtomicInteger_getAndDecrement:
      case TR::java_util_concurrent_atomic_AtomicInteger_getAndIncrement:
      case TR::java_util_concurrent_atomic_AtomicInteger_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicInteger_incrementAndGet:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_addAndGet:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_decrementAndGet:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndAdd:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndDecrement:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndIncrement:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_incrementAndGet:
         size = 4;
         break;
      case TR::java_util_concurrent_atomic_AtomicReference_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicReferenceArray_getAndSet:
         size = TR::Compiler->om.sizeofReferenceAddress();
         break;
      default:
         size = 8;
         }

      switch (currentMethod)
         {
      case TR::java_util_concurrent_atomic_AtomicBoolean_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicInteger_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicLong_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicLongArray_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicReference_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicReferenceArray_getAndSet:
         isAddOp = false;
      case TR::java_util_concurrent_atomic_AtomicInteger_getAndAdd:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndAdd:
      case TR::java_util_concurrent_atomic_AtomicLong_getAndAdd:
      case TR::java_util_concurrent_atomic_AtomicLongArray_getAndAdd:
         isDelta = true;
      case TR::java_util_concurrent_atomic_AtomicInteger_getAndDecrement:
      case TR::java_util_concurrent_atomic_AtomicInteger_getAndIncrement:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndDecrement:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndIncrement:
      case TR::java_util_concurrent_atomic_AtomicLong_getAndDecrement:
      case TR::java_util_concurrent_atomic_AtomicLong_getAndIncrement:
      case TR::java_util_concurrent_atomic_AtomicLongArray_getAndDecrement:
      case TR::java_util_concurrent_atomic_AtomicLongArray_getAndIncrement:
         isGetAndOp = true;
         break;
      case TR::java_util_concurrent_atomic_AtomicInteger_addAndGet:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_addAndGet:
      case TR::java_util_concurrent_atomic_AtomicLong_addAndGet:
      case TR::java_util_concurrent_atomic_AtomicLongArray_addAndGet:
         isDelta = true;
         break;
      default:
         break;
      }

   isDeltaImplied = isAddOp && !isDelta;

   if (isDeltaImplied)
      {
      switch (currentMethod)
         {
      case TR::java_util_concurrent_atomic_AtomicInteger_decrementAndGet:
      case TR::java_util_concurrent_atomic_AtomicInteger_getAndDecrement:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_decrementAndGet:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndDecrement:
      case TR::java_util_concurrent_atomic_AtomicLong_decrementAndGet:
      case TR::java_util_concurrent_atomic_AtomicLong_getAndDecrement:
      case TR::java_util_concurrent_atomic_AtomicLongArray_decrementAndGet:
      case TR::java_util_concurrent_atomic_AtomicLongArray_getAndDecrement:
         delta = (int32_t) - 1;
      default:
         delta = (int32_t) 1;
         }
      }

   if (isUnsafe && isCAS)
      {
      firstChild = node->getChild(0);
      valueChild = node->getChild(1);
      indexChild = node->getChild(2);
      if (isRefWrite)
         {
         expRefChild = node->getChild(3);
         newRefChild = node->getChild(4);
         }
      else
         {
         expValChild = node->getChild(3);
         newValChild = node->getChild(4);
         }
      }
   else
      {
      valueChild = node->getChild(0);
      idx = 1;
      if (isArray)
         {
         indexChild = node->getChild(idx++);
         }
      if (isDelta)
         {
         deltaChild = node->getChild(idx++);
         }
      else if (isCAS)
         {
         expValChild = node->getChild(idx);
         newValChild = node->getChild(idx + 1);
         idx += 2;
         }
      if (idx != node->getNumChildren())
         {
         TR_ASSERT(0, "Wrong number of children for JUC Atomic node\n");
         return NULL;
         }
      }

   valueReg = cg->evaluate(valueChild);
   objReg = valueReg;

   if (isUnsafe)
      {
      if (indexChild->getOpCode().isLoadConst() && indexChild->getRegister() == NULL && comp->target().is32Bit())
         {
         fieldOffset = (int32_t) indexChild->getLongInt();
         //traceMsg(comp,"Allocate fieldOffsetReg\n");
         fieldOffsetReg = cg->allocateRegister();
         numDeps++;
         loadConstant(cg, node, fieldOffset, fieldOffsetReg);
         }
      else
         {
         fieldOffsetReg = cg->evaluate(indexChild);
         resultReg = cg->allocateRegister();
         numDeps += 2;
         if (comp->target().is32Bit())
            fieldOffsetReg = fieldOffsetReg->getLowOrder();
         }
      }
   else if (!isArray)
      {
      TR_OpaqueClassBlock *classBlock;
      const char *className, *fieldSig;
      int32_t classNameLen, fieldSigLen;
      fieldSigLen = 1;

      switch (currentMethod)
         {
      case TR::java_util_concurrent_atomic_AtomicBoolean_getAndSet:
         className = "Ljava/util/concurrent/atomic/AtomicBoolean;";
         classNameLen = 43;
         fieldSig = "I"; // not a type, the field is int
         break;
      case TR::java_util_concurrent_atomic_AtomicInteger_addAndGet:
      case TR::java_util_concurrent_atomic_AtomicInteger_decrementAndGet:
      case TR::java_util_concurrent_atomic_AtomicInteger_getAndAdd:
      case TR::java_util_concurrent_atomic_AtomicInteger_getAndDecrement:
      case TR::java_util_concurrent_atomic_AtomicInteger_getAndIncrement:
      case TR::java_util_concurrent_atomic_AtomicInteger_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicInteger_incrementAndGet:
         className = "Ljava/util/concurrent/atomic/AtomicInteger;";
         classNameLen = 43;
         fieldSig = "I";
         break;
      case TR::java_util_concurrent_atomic_AtomicLong_addAndGet:
      case TR::java_util_concurrent_atomic_AtomicLong_decrementAndGet:
      case TR::java_util_concurrent_atomic_AtomicLong_getAndAdd:
      case TR::java_util_concurrent_atomic_AtomicLong_getAndDecrement:
      case TR::java_util_concurrent_atomic_AtomicLong_getAndIncrement:
      case TR::java_util_concurrent_atomic_AtomicLong_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicLong_incrementAndGet:
         className = "Ljava/util/concurrent/atomic/AtomicLong;";
         classNameLen = 40;
         fieldSig = "J";
         break;
      case TR::java_util_concurrent_atomic_AtomicReference_getAndSet:
         className = "Ljava/util/concurrent/atomic/AtomicReference;";
         classNameLen = 45;
         fieldSig = "Ljava/lang/Object;";
         fieldSigLen = 18;
         break;
      default:
         TR_ASSERT(0, "Unknown atomic operation method\n");
         return NULL;
         }
      classBlock = fej9->getClassFromSignature(className, classNameLen, comp->getCurrentMethod(), true);
      fieldOffset = fej9->getObjectHeaderSizeInBytes() + fej9->getInstanceFieldOffset(classBlock, "value", 5, fieldSig, fieldSigLen);
      }
   else // isArray
      {
      TR::Register *indexReg = cg->evaluate(indexChild);
      int32_t shiftAmount = size == 8 ? 3 : 2;
      fieldOffset = TR::Compiler->om.contiguousArrayHeaderSizeInBytes();

      TR_OpaqueClassBlock *classBlock;
      const char *className, *fieldSig;
      int32_t classNameLen, fieldSigLen;
      fieldSigLen = 1;

      switch (currentMethod)
         {
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_addAndGet:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_decrementAndGet:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndAdd:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndDecrement:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndIncrement:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_incrementAndGet:
         className = "Ljava/util/concurrent/atomic/AtomicIntegerArray;";
         classNameLen = 48;
         fieldSig = "[I";
         fieldSigLen = 2;
         break;
      case TR::java_util_concurrent_atomic_AtomicLongArray_addAndGet:
      case TR::java_util_concurrent_atomic_AtomicLongArray_decrementAndGet:
      case TR::java_util_concurrent_atomic_AtomicLongArray_getAndAdd:
      case TR::java_util_concurrent_atomic_AtomicLongArray_getAndDecrement:
      case TR::java_util_concurrent_atomic_AtomicLongArray_getAndIncrement:
      case TR::java_util_concurrent_atomic_AtomicLongArray_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicLongArray_incrementAndGet:
         className = "Ljava/util/concurrent/atomic/AtomicLongArray;";
         classNameLen = 45;
         fieldSig = "[J";
         fieldSigLen = 2;
         break;
      case TR::java_util_concurrent_atomic_AtomicReferenceArray_getAndSet:
         className = "Ljava/util/concurrent/atomic/AtomicReferenceArray;";
         classNameLen = 50;
         fieldSig = "[Ljava/lang/Object;";
         fieldSigLen = 19;
         break;
      default:
         TR_ASSERT(0, "Unknown atomic operation method\n");
         return NULL;
         }
      classBlock = fej9->getClassFromSignature(className, classNameLen, comp->getCurrentMethod(), true);
      int32_t arrayFieldOffset = fej9->getObjectHeaderSizeInBytes() + fej9->getInstanceFieldOffset(classBlock, "array", 5, fieldSig, fieldSigLen);

      TR::MemoryReference *tempMR = TR::MemoryReference::createWithDisplacement(cg, valueReg, arrayFieldOffset, size);
      scratchReg = cg->allocateCollectedReferenceRegister();

      if (TR::Compiler->om.compressObjectReferences())
         // read only 32 bits
         generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, scratchReg,
               TR::MemoryReference::createWithDisplacement(cg, valueReg, arrayFieldOffset, 4));
      else
         generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, scratchReg, TR::MemoryReference::createWithDisplacement(cg, valueReg, arrayFieldOffset, TR::Compiler->om.sizeofReferenceAddress()));

      valueReg = scratchReg;
      fieldOffsetReg = cg->allocateRegister();
      numDeps += 2;
      generateShiftLeftImmediate(cg, node, fieldOffsetReg, indexReg, shiftAmount);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi2, node, fieldOffsetReg, fieldOffsetReg, fieldOffset);
      }

   if (isUnsafe)
      {
      //right now not checking for ref, just doing long!
      if (isRefWrite)
         {
         }
      else
         {
         if (expValChild->getOpCode().isLoadConst() && expValChild->getRegister() == NULL)
            {
            if (size == 8)
               expValImm = expValChild->getLongInt();
            else
               expValImm = expValChild->getInt();
            if (expValImm >= LOWER_IMMED && expValImm <= UPPER_IMMED)
               isArgImm = true;
            }

         if (!isArgImm)
            {
            expValReg = cg->evaluate(expValChild);
            numDeps++;
            }

         newValReg = cg->evaluate(newValChild);
         numDeps++;
         }
      }

   if (isCAS)
      {
      TR::LabelSymbol *rsvFailLabel;
      cndReg = cg->allocateRegister(TR_CCR);
      currentReg = cg->allocateRegister();
      numDeps += 2;
      startLabel = TR::LabelSymbol::create(cg->trHeapMemory(),cg);
      failLabel = TR::LabelSymbol::create(cg->trHeapMemory(),cg);
      doneLabel = TR::LabelSymbol::create(cg->trHeapMemory(),cg);

      //allow spurious failures
      rsvFailLabel = isWeak ? failLabel : startLabel;

      startLabel->setStartInternalControlFlow();

      if (fieldOffsetReg == NULL)
         {
         numDeps++;
         fieldOffsetReg = cg->allocateRegister();
         loadConstant(cg, node, fieldOffset, fieldOffsetReg);
         }
      if (resultReg == NULL)
         {
         resultReg = fieldOffsetReg;
         }

      if (!isWeak)
         generateInstruction(cg, TR::InstOpCode::lwsync, node);

      generateLabelInstruction(cg, TR::InstOpCode::label, node, startLabel);

      generateTrg1MemInstruction(cg, size == 8 ? TR::InstOpCode::ldarx : TR::InstOpCode::lwarx, node, currentReg,
            TR::MemoryReference::createWithIndexReg(cg, valueReg, (fieldOffsetReg->getRegisterPair() ? fieldOffsetReg->getLowOrder() : fieldOffsetReg), size));

      if (size == 8 && comp->target().is32Bit())
         {
         if (!isArgImm && expValReg->getRegisterPair())
            {
            generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldimi, node, expValReg->getLowOrder(), expValReg->getHighOrder(), 32, CONSTANT64(0xFFFFFFFF00000000));
            expPair = true;
            numDeps++;
            }
         if (newValReg->getRegisterPair())
            {
            if (newValReg != expValReg)
               {
               generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldimi, node, newValReg->getLowOrder(), newValReg->getHighOrder(), 32, CONSTANT64(0xFFFFFFFF00000000));
               }
            numDeps++;
            newPair = true;
            }
         }

      if (!isArgImm && expComposedReg == NULL)
         {
         if (expPair)
            expComposedReg = expValReg->getLowOrder();
         else
            expComposedReg = expValReg != NULL ? expValReg : expRefReg;
         }
      if (newComposedReg == NULL)
         {
         if (newPair)
            newComposedReg = newValReg->getLowOrder();
         else
            newComposedReg = newValReg != NULL ? newValReg : newRefReg;
         }

      if (isArgImm)
         {
         generateTrg1Src1ImmInstruction(cg, size == 8 ? TR::InstOpCode::cmpi8 : TR::InstOpCode::cmpi4, node, cndReg, currentReg, expValImm);
         }
      else
         {
         generateTrg1Src2Instruction(cg, size == 8 ? TR::InstOpCode::cmp8 : TR::InstOpCode::cmp4, node, cndReg, currentReg, expComposedReg);
         }

      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, failLabel, cndReg);
      generateMemSrc1Instruction(cg, size == 8 ? TR::InstOpCode::stdcx_r : TR::InstOpCode::stwcx_r, node,
            TR::MemoryReference::createWithIndexReg(cg, valueReg, (fieldOffsetReg->getRegisterPair() ? fieldOffsetReg->getLowOrder() : fieldOffsetReg), size),
            newComposedReg);

      /* Expect store to be successful, so this branch is usually not taken */
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, PPCOpProp_BranchUnlikely, node, rsvFailLabel, cndReg);

      if (!isWeak)
         generateInstruction(cg, TR::InstOpCode::sync, node);

      generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, 1);
      generateLabelInstruction(cg, TR::InstOpCode::b, node, doneLabel);
      generateLabelInstruction(cg, TR::InstOpCode::label, node, failLabel);

      if (!isWeak && size == 8 && comp->target().is32Bit())
         {
         /* store original current value conditionally
          * if it succeeds, then this was a true failure, if it
          * does not, it could have been corruption messing up the compare
          */
         generateMemSrc1Instruction(cg, size == 8 ? TR::InstOpCode::stdcx_r : TR::InstOpCode::stwcx_r, node,
               TR::MemoryReference::createWithIndexReg(cg, valueReg, (fieldOffsetReg->getRegisterPair() ? fieldOffsetReg->getLowOrder() : fieldOffsetReg), size),
               currentReg);
         /* Expect store to be successful, so this branch is usually not taken */
         generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, PPCOpProp_BranchUnlikely, node, startLabel, cndReg);
         }

      generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, 0);

      }

   TR::RegisterDependencyConditions *conditions;
   conditions = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, numDeps, cg->trMemory());
   conditions->addPostCondition(valueReg, TR::RealRegister::NoReg);
   conditions->getPostConditions()->getRegisterDependency(0)->setExcludeGPR0();
   conditions->addPostCondition(currentReg, TR::RealRegister::NoReg);

   numDeps -= 2;
   if (fieldOffsetReg != NULL)
      {
      conditions->addPostCondition(fieldOffsetReg, TR::RealRegister::NoReg);
      numDeps--;
      }
   if (resultReg != fieldOffsetReg)
      {
      conditions->addPostCondition(resultReg, TR::RealRegister::NoReg);
      numDeps--;
      }
   if (objReg != valueReg)
      {
      conditions->addPostCondition(objReg, TR::RealRegister::NoReg);
      numDeps--;
      }
   if (isUnsafe)
      {
      if (isRefWrite)
         {
         conditions->addPostCondition(newRefReg, TR::RealRegister::NoReg);
         conditions->addPostCondition(expRefReg, TR::RealRegister::NoReg);
         numDeps -= 2;
         }
      else
         {
         if (newPair)
            {
            conditions->addPostCondition(newValReg->getHighOrder(), TR::RealRegister::NoReg);
            conditions->addPostCondition(newValReg->getLowOrder(), TR::RealRegister::NoReg);
            numDeps--;
            }
         else
            {
            conditions->addPostCondition(newValReg, TR::RealRegister::NoReg);
            }
         numDeps--;
         if (!isArgImm)
            {
            if (expPair)
               {
               conditions->addPostCondition(expValReg->getHighOrder(), TR::RealRegister::NoReg);
               conditions->addPostCondition(expValReg->getLowOrder(), TR::RealRegister::NoReg);
               numDeps--;
               }
            else
               {
               conditions->addPostCondition(expValReg, TR::RealRegister::NoReg);
               }
            numDeps--;
            }
         }
      }

   conditions->addPostCondition(cndReg, TR::RealRegister::cr0);
   numDeps--;

   doneLabel->setEndInternalControlFlow();
   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel, conditions);

   if (firstChild)
      cg->decReferenceCount(firstChild);
   cg->decReferenceCount(valueChild);
   cg->decReferenceCount(indexChild);
   cg->decReferenceCount(expValChild);
   cg->decReferenceCount(newValChild);
   cg->stopUsingRegister(cndReg);
   cg->stopUsingRegister(currentReg);
   node->setRegister(resultReg);

   return resultReg;
   }

static TR::Register *inlineConcurrentLinkedQueueTMOffer(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (comp->fe());
   int32_t addressFieldSize = TR::Compiler->om.sizeofReferenceField();
   TR::InstOpCode::Mnemonic loadOpCode = (addressFieldSize == 8) ? TR::InstOpCode::ld : TR::InstOpCode::lwz;
   TR::InstOpCode::Mnemonic storeOpCode = (addressFieldSize == 8) ? TR::InstOpCode::std : TR::InstOpCode::stw;
   bool usesCompressedrefs = comp->useCompressedPointers();

   // wrt bar
   auto gcMode = TR::Compiler->om.writeBarrierType();
   bool doWrtBar = (gcMode == gc_modron_wrtbar_satb || gcMode == gc_modron_wrtbar_oldcheck || gcMode == gc_modron_wrtbar_cardmark_and_oldcheck || gcMode == gc_modron_wrtbar_always
         || TR::Options::getCmdLineOptions()->realTimeGC());
   bool doCrdMrk = ((gcMode == gc_modron_wrtbar_cardmark || gcMode == gc_modron_wrtbar_cardmark_and_oldcheck || gcMode == gc_modron_wrtbar_cardmark_incremental) && (!node->getOpCode().isWrtBar() || !node->isNonHeapObjectWrtBar()));

   TR_OpaqueClassBlock * classBlock = NULL;
   classBlock = fej9->getClassFromSignature("Ljava/util/concurrent/ConcurrentLinkedQueue$Node;", 49, comp->getCurrentMethod(), true);
   int32_t offsetNext = fej9->getObjectHeaderSizeInBytes() + fej9->getInstanceFieldOffset(classBlock, "next", 4, "Ljava/util/concurrent/ConcurrentLinkedQueue$Node;", 49);
   classBlock = fej9->getClassFromSignature("Ljava/util/concurrent/ConcurrentLinkedQueue;", 44, comp->getCurrentMethod(), true);
   int32_t offsetTail = fej9->getObjectHeaderSizeInBytes() + fej9->getInstanceFieldOffset(classBlock, "tail", 4, "Ljava/util/concurrent/ConcurrentLinkedQueue$Node;", 49);

   TR::Register * cndReg = cg->allocateRegister(TR_CCR);
   TR::Register * resultReg = cg->allocateRegister();
   TR::Register * objReg = cg->evaluate(node->getFirstChild());
   TR::Register * nReg = cg->evaluate(node->getSecondChild());
   TR::Register * pReg = cg->allocateRegister();
   TR::Register * qReg = cg->allocateRegister();
   TR::Register * retryCountReg = cg->allocateRegister();
   TR::Register * temp3Reg = NULL;
   TR::Register * temp4Reg = NULL;

   TR::LabelSymbol * loopLabel = TR::LabelSymbol::create(cg->trHeapMemory(),cg);
   TR::LabelSymbol * insertLabel = TR::LabelSymbol::create(cg->trHeapMemory(),cg);
   TR::LabelSymbol * failureLabel = TR::LabelSymbol::create(cg->trHeapMemory(),cg);
   TR::LabelSymbol * returnLabel = TR::LabelSymbol::create(cg->trHeapMemory(),cg);
   TR::LabelSymbol * wrtbar1Donelabel = TR::LabelSymbol::create(cg->trHeapMemory(),cg);

   int numDeps = doWrtBar? 11 : 9; //two extra deps for space boundaries (used in wrtbar)

   TR::RegisterDependencyConditions *conditions = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(numDeps, numDeps, cg->trMemory());

   TR::addDependency(conditions, cndReg, TR::RealRegister::cr0, TR_CCR, cg);
   TR::addDependency(conditions, resultReg, TR::RealRegister::NoReg, TR_GPR, cg);
   TR::addDependency(conditions, objReg, TR::RealRegister::NoReg, TR_GPR, cg);
   TR::addDependency(conditions, pReg, TR::RealRegister::gr3, TR_GPR, cg); // dstReg for wrtbar
   TR::addDependency(conditions, qReg, TR::RealRegister::gr11, TR_GPR, cg); // temp1Reg for wrtbar
   TR::addDependency(conditions, retryCountReg, TR::RealRegister::NoReg, TR_GPR, cg); // temp2Reg for wrtbar
   TR::addDependency(conditions, nReg, TR::RealRegister::gr4, TR_GPR, cg); // srcReg for wrtbar

   static char * disableTMOffer = feGetEnv("TR_DisableTMOffer");
   static char * debugTM = feGetEnv("TR_DebugTM");

   /*
    * TM is not compatible with read barriers. If read barriers are required, TM is disabled.
    */
   if (disableTMOffer || TR::Compiler->om.readBarrierType() != gc_modron_readbar_none)
      {
      generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, 3); // TM offer disabled
      generateLabelInstruction(cg, TR::InstOpCode::b, node, returnLabel);
      }

   if (debugTM)
      {
      printf("\nTM: use TM CLQ.Offer in %s (%s)", comp->signature(), comp->getHotnessName(comp->getMethodHotness()));
      fflush (stdout);
      }

   static const char *s = feGetEnv("TR_TMOfferRetry");
   static uint16_t TMOfferRetry = s ? atoi(s) : 7;

   //#define CLQ_OFFER_RETRY 7
   generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, retryCountReg, TMOfferRetry);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, loopLabel);

   generateInstruction(cg, TR::InstOpCode::tbegin_r, node);

   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, failureLabel, cndReg);

   // ALG: p := this.tail
   generateTrg1MemInstruction(cg, loadOpCode, node, pReg, TR::MemoryReference::createWithDisplacement(cg, objReg, offsetTail, addressFieldSize));
   if (usesCompressedrefs)
      {
      genDecompressPointerWithTempReg(cg, node, pReg, resultReg, NULL, false); // p is not null
      }

   // ALG: q := p.next
   generateTrg1MemInstruction(cg, loadOpCode, node, qReg, TR::MemoryReference::createWithDisplacement(cg, pReg, offsetNext, addressFieldSize));

   // ALG: if q == null goto insertLabel
   generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpli, node, cndReg, qReg, 0);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, insertLabel, cndReg);

   if (usesCompressedrefs)
      {
      genDecompressPointerWithTempReg(cg, node, qReg, resultReg, NULL, false); // q has been checked not null
      }

   // ALG: p := q
   generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, pReg, qReg);

   // ALG: q := p.next
   generateTrg1MemInstruction(cg, loadOpCode, node, qReg, TR::MemoryReference::createWithDisplacement(cg, pReg, offsetNext, addressFieldSize));

   // ALG: if q == null goto insertLabel
   generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpli, node, cndReg, qReg, 0);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, PPCOpProp_BranchLikely, node, insertLabel, cndReg);

   // ALG: tend.
   generateInstruction(cg, TR::InstOpCode::tend_r, node);

   // ALG: res := 1
   // cannot find tail
   generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, 1);

   // ALG: goto returnLabel
   generateLabelInstruction(cg, TR::InstOpCode::b, node, returnLabel);

   // ALG: *** insert:
   generateLabelInstruction(cg, TR::InstOpCode::label, node, insertLabel);

   // need uncompressed nReg for the wrtbar
   TR::Register *compressedReg = nReg;
   if (usesCompressedrefs)
      {
      compressedReg = genCompressPointerNonNull2RegsWithTempReg(cg, node, nReg, qReg, resultReg); //In Java code checkNotNull() already ensures n != null
      }
   // ALG: p.next := n
   generateMemSrc1Instruction(cg, storeOpCode, node, TR::MemoryReference::createWithDisplacement(cg, pReg, offsetNext, addressFieldSize), compressedReg);

   // ALG: this.tail = n
   generateMemSrc1Instruction(cg, storeOpCode, node, TR::MemoryReference::createWithDisplacement(cg, objReg, offsetTail, addressFieldSize), compressedReg);

   // ALG: tend
   generateInstruction(cg, TR::InstOpCode::tend_r, node);

   // ALG: res := 0
   // TM success
   generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, 0);

   if (doWrtBar)
      {
      if (doCrdMrk)
         {
         temp3Reg = cg->allocateRegister();
         temp4Reg = cg->allocateRegister();
         TR::addDependency(conditions, temp3Reg, TR::RealRegister::NoReg, TR_GPR, cg);
         TR::addDependency(conditions, temp4Reg, TR::RealRegister::NoReg, TR_GPR, cg);
         conditions->getPostConditions()->getRegisterDependency(5)->setExcludeGPR0(); //5=temp2Reg
         }

      VMnonNullSrcWrtBarCardCheckEvaluator(node, nReg, pReg, cndReg, qReg, retryCountReg, temp3Reg, temp4Reg, wrtbar1Donelabel, conditions, false, cg, false);
      // ALG: *** wrtbar1Donelabel
      generateDepLabelInstruction(cg, TR::InstOpCode::label, node, wrtbar1Donelabel, conditions);

      generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, pReg, objReg);

      VMnonNullSrcWrtBarCardCheckEvaluator(node, nReg, pReg, cndReg, qReg, retryCountReg, temp3Reg, temp4Reg, returnLabel, conditions, false, cg, false);

      }
   else if (doCrdMrk)
      {
      temp3Reg = cg->allocateRegister();
      TR::addDependency(conditions, temp3Reg, TR::RealRegister::NoReg, TR_GPR, cg);
      conditions->getPostConditions()->getRegisterDependency(3)->setExcludeGPR0(); //3=dstReg
      conditions->getPostConditions()->getRegisterDependency(4)->setExcludeGPR0(); //4=temp1Reg

      VMCardCheckEvaluator(node, pReg, cndReg, qReg, retryCountReg, temp3Reg, conditions, cg);

      generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, pReg, objReg);

      VMCardCheckEvaluator(node, pReg, cndReg, qReg, retryCountReg, temp3Reg, conditions, cg);
      }

   // ALG: goto returnLabel
   generateLabelInstruction(cg, TR::InstOpCode::b, node, returnLabel);

   // ALG: *** failureLabel
   generateLabelInstruction(cg, TR::InstOpCode::label, node, failureLabel);
   //generateDepLabelInstruction(cg, TR::InstOpCode::label, node, failureLabel, conditions);

   // retryCount-=1
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addic_r, node, retryCountReg, retryCountReg, -1);

   // ALG: goto loopLabel
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, loopLabel, cndReg);

   // ALG: res := 2
   // exceeds retry count
   generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, 2);

   // ALG: *** return
   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, returnLabel, conditions);

   // wrtbar or after TM success earlier

   cg->decReferenceCount(node->getFirstChild());
   cg->decReferenceCount(node->getSecondChild());
   cg->stopUsingRegister(pReg);
   cg->stopUsingRegister(qReg);
   cg->stopUsingRegister(retryCountReg);
   cg->stopUsingRegister(cndReg);
   if (temp3Reg != NULL)
      cg->stopUsingRegister(temp3Reg);
   if (temp4Reg != NULL)
      cg->stopUsingRegister(temp4Reg);

   node->setRegister(resultReg);
   resultReg->setContainsCollectedReference();
   return resultReg;
   }

static TR::Register *inlineConcurrentLinkedQueueTMPoll(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (comp->fe());
   int32_t addressFieldSize = TR::Compiler->om.sizeofReferenceField();
   TR::InstOpCode::Mnemonic loadOpCode = (addressFieldSize == 8) ? TR::InstOpCode::ld : TR::InstOpCode::lwz;
   TR::InstOpCode::Mnemonic storeOpCode = (addressFieldSize == 8) ? TR::InstOpCode::std : TR::InstOpCode::stw;
   bool usesCompressedrefs = comp->useCompressedPointers();

   TR_OpaqueClassBlock *classBlock = fej9->getClassFromSignature("Ljava/util/concurrent/ConcurrentLinkedQueue;", 44, comp->getCurrentMethod(), true);
   int32_t offsetHead = fej9->getObjectHeaderSizeInBytes() + fej9->getInstanceFieldOffset(classBlock, "head", 4, "Ljava/util/concurrent/ConcurrentLinkedQueue$Node;", 49);
   classBlock = fej9->getClassFromSignature("Ljava/util/concurrent/ConcurrentLinkedQueue$Node;", 49, comp->getCurrentMethod(), true);
   int32_t offsetNext = fej9->getObjectHeaderSizeInBytes() + fej9->getInstanceFieldOffset(classBlock, "next", 4, "Ljava/util/concurrent/ConcurrentLinkedQueue$Node;", 49);
   int32_t offsetItem = fej9->getObjectHeaderSizeInBytes() + fej9->getInstanceFieldOffset(classBlock, "item", 4, "Ljava/lang/Object;", 18);

   TR::Register * objReg = cg->evaluate(node->getFirstChild());
   TR::Register * cndReg = cg->allocateRegister(TR_CCR);
   TR::Register * resultReg = cg->allocateRegister();
   TR::Register * nullReg = cg->allocateRegister();
   TR::Register * pReg = cg->allocateRegister();
   TR::Register * qReg = cg->allocateRegister();
   TR::Register * temp3Reg = cg->allocateRegister();
   TR::Register * temp4Reg = NULL;

   TR::LabelSymbol * tendLabel = TR::LabelSymbol::create(cg->trHeapMemory(),cg);
   TR::LabelSymbol * returnLabel = TR::LabelSymbol::create(cg->trHeapMemory(),cg);
   TR::LabelSymbol * failureLabel = TR::LabelSymbol::create(cg->trHeapMemory(),cg);

   TR::RegisterDependencyConditions *conditions = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(8, 8, cg->trMemory());

   TR::addDependency(conditions, cndReg, TR::RealRegister::cr0, TR_CCR, cg);
   TR::addDependency(conditions, resultReg, TR::RealRegister::NoReg, TR_GPR, cg);
   TR::addDependency(conditions, objReg, TR::RealRegister::gr3, TR_GPR, cg); // dstReg for wrtbar
   TR::addDependency(conditions, nullReg, TR::RealRegister::gr11, TR_GPR, cg); // temp1Reg for wrtbar
   TR::addDependency(conditions, pReg, TR::RealRegister::NoReg, TR_GPR, cg); // temp2Reg for wrtbar
   TR::addDependency(conditions, qReg, TR::RealRegister::gr4, TR_GPR, cg); // srcReg for wrtbar
   TR::addDependency(conditions, temp3Reg, TR::RealRegister::NoReg, TR_GPR, cg);

   static char * disableTMPoll = feGetEnv("TR_DisableTMPoll");
   static char * debugTM = feGetEnv("TR_DebugTM");

   /*
    * TM is not compatible with read barriers. If read barriers are required, TM is disabled.
    */
   if (disableTMPoll || TR::Compiler->om.readBarrierType() != gc_modron_readbar_none)
      {
      generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, 0); // BEFORE WAS 0
      generateLabelInstruction(cg, TR::InstOpCode::b, node, returnLabel);
      }

   if (debugTM)
      {
      printf("\nTM: use TM CLQ.Poll in %s (%s)", comp->signature(), comp->getHotnessName(comp->getMethodHotness()));
      fflush (stdout);
      }

   generateInstruction(cg, TR::InstOpCode::tbegin_r, node);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, failureLabel, cndReg);

   // ALG: p := this.head
   generateTrg1MemInstruction(cg, loadOpCode, node, temp3Reg, TR::MemoryReference::createWithDisplacement(cg, objReg, offsetHead, addressFieldSize));
   TR::Register *decompressedP = temp3Reg;
   if (usesCompressedrefs)
      {
      decompressedP = genDecompressPointerNonNull2RegsWithTempReg(cg, node, temp3Reg, pReg, qReg);
      }

   // ALG: res := p.item
   generateTrg1MemInstruction(cg, loadOpCode, node, resultReg, TR::MemoryReference::createWithDisplacement(cg, decompressedP, offsetItem, addressFieldSize));
   if (usesCompressedrefs)
      {
      genDecompressPointerWithTempReg(cg, node, resultReg, qReg, cndReg); // NOTE: res could be null
      }

   // ALG: q := p.next
   generateTrg1MemInstruction(cg, loadOpCode, node, qReg, TR::MemoryReference::createWithDisplacement(cg, decompressedP, offsetNext, addressFieldSize));

   // ALG: p.item := null
   generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, nullReg, 0);

   generateMemSrc1Instruction(cg, storeOpCode, node, TR::MemoryReference::createWithDisplacement(cg, decompressedP, offsetItem, addressFieldSize), nullReg);

   // ALG: if q == null goto tendLabel
   generateTrg1Src1ImmInstruction(cg,TR::InstOpCode::Op_cmpli, node, cndReg, qReg, 0);

   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, tendLabel, cndReg);

   // ALG: this.head = q
   // qReg is still compressed, use it to store
   generateMemSrc1Instruction(cg, storeOpCode, node, TR::MemoryReference::createWithDisplacement(cg, objReg, offsetHead, addressFieldSize), qReg);

   if (usesCompressedrefs)
      {
      genDecompressPointerWithTempReg(cg, node, qReg, nullReg, NULL, false); // qReg is not null
      }

   // ALG: p.next := p
   generateMemSrc1Instruction(cg, storeOpCode, node, TR::MemoryReference::createWithDisplacement(cg, decompressedP, offsetNext, addressFieldSize), temp3Reg);

   // ALG: tend
   generateInstruction(cg, TR::InstOpCode::tend_r, node);

   // WrtBar for this.head = q
   auto gcMode = TR::Compiler->om.writeBarrierType();
   bool doWrtBar = (gcMode == gc_modron_wrtbar_satb || gcMode == gc_modron_wrtbar_oldcheck || gcMode == gc_modron_wrtbar_cardmark_and_oldcheck || gcMode == gc_modron_wrtbar_always
         || comp->getOptions()->realTimeGC());
   bool doCrdMrk = ((gcMode == gc_modron_wrtbar_cardmark || gcMode == gc_modron_wrtbar_cardmark_and_oldcheck || gcMode == gc_modron_wrtbar_cardmark_incremental) && (!node->getOpCode().isWrtBar() || !node->isNonHeapObjectWrtBar()));
   if (doWrtBar)
      {
      if (doCrdMrk)
         {
         temp4Reg = cg->allocateRegister();
         TR::addDependency(conditions, temp4Reg, TR::RealRegister::NoReg, TR_GPR, cg);
         conditions->getPostConditions()->getRegisterDependency(4)->setExcludeGPR0(); //4=temp2Reg
         }

      VMnonNullSrcWrtBarCardCheckEvaluator(node, qReg, objReg, cndReg, nullReg, pReg, temp3Reg, temp4Reg, returnLabel, conditions, false, cg, false);
      }
   else if (doCrdMrk)
      {
      conditions->getPostConditions()->getRegisterDependency(2)->setExcludeGPR0(); //2=dstReg
      conditions->getPostConditions()->getRegisterDependency(3)->setExcludeGPR0(); //3=temp1Reg

      VMCardCheckEvaluator(node, objReg, cndReg, nullReg, pReg, temp3Reg, conditions, cg);
      }

   // ALG: goto returnLabel
   generateLabelInstruction(cg, TR::InstOpCode::b, node, returnLabel);

   // ALG:**** tendLabel:
   generateLabelInstruction(cg, TR::InstOpCode::label, node, tendLabel);

   // ALG: tend
   generateInstruction(cg, TR::InstOpCode::tend_r, node);

   // ALG: goto returnLabel
   generateLabelInstruction(cg, TR::InstOpCode::b, node, returnLabel);

   // ALG: *** failureLabel
   generateLabelInstruction(cg, TR::InstOpCode::label, node, failureLabel);
   // resultReg := 0
   generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, 0);

   // ALG: *** returnLabel
   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, returnLabel, conditions);

   // wrtbar if result is non-null

   cg->decReferenceCount(node->getFirstChild());
   cg->stopUsingRegister(nullReg);
   cg->stopUsingRegister(pReg);
   cg->stopUsingRegister(qReg);
   cg->stopUsingRegister(cndReg);
   if (temp3Reg != NULL)
      cg->stopUsingRegister(temp3Reg);
   if (temp4Reg != NULL)
      cg->stopUsingRegister(temp4Reg);

   node->setRegister(resultReg);
   resultReg->setContainsCollectedReference();
   return resultReg;
   }

#if defined(AIXPPC)
static TR::Register *dangerousGetCPU(
      TR::Node *node,
      TR::CodeGenerator *cg)
   {
   // This is a bad idea that works most of the time.
   // We don't bother with ANY of the usual native method protocol(!)
   // But we do adjust r1 to be way out of the way and then call
   // "mycpu" which is buried deep in the kernel

   TR::Register *retReg = cg->allocateRegister();
   TR::Register *gr1 = cg->allocateRegister();
   TR::Register *gr2 = cg->allocateRegister();
   TR::Register *tmpReg = cg->allocateRegister();
   TR::RegisterDependencyConditions *dependencies = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(4, 4, cg->trMemory());
   TR::Compilation *comp = cg->comp();
   TR::addDependency(dependencies, retReg, TR::RealRegister::gr3, TR_GPR, cg);
   TR::addDependency(dependencies, gr1, TR::RealRegister::gr1, TR_GPR, cg);
   TR::addDependency(dependencies, gr2, TR::RealRegister::gr2, TR_GPR, cg);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, gr1, gr1, -256);
   generateMemSrc1Instruction(cg,TR::InstOpCode::Op_st, node,
         TR::MemoryReference::createWithDisplacement(cg, gr1, 0x14, TR::Compiler->om.sizeofReferenceAddress()),
         gr2);
   intptr_t xx[3];
   xx[0] = ((intptr_t *)(void *)&mycpu)[0];
   xx[1] = ((intptr_t *)(void *)&mycpu)[1];
   xx[2] = ((intptr_t *)(void *)&mycpu)[2];
   loadAddressConstant(cg, comp->compileRelocatableCode(), node, xx[0], tmpReg);
   loadAddressConstant(cg, comp->compileRelocatableCode(), node, xx[1], gr2);
   generateSrc1Instruction(cg, TR::InstOpCode::mtctr, node, tmpReg);
   generateInstruction(cg, TR::InstOpCode::bctrl, node);

   generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, gr2,
         TR::MemoryReference::createWithDisplacement(cg, gr1, 0x14, TR::Compiler->om.sizeofReferenceAddress()));
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, gr1, gr1, 256);
   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, TR::LabelSymbol::create(cg->trHeapMemory(),cg), dependencies);
   cg->stopUsingRegister(gr1);
   cg->stopUsingRegister(gr2);
   cg->stopUsingRegister(tmpReg);
   node->setRegister(retReg);
   cg->machine()->setLinkRegisterKilled(true);
   return retReg;
   }
#else
static TR::Register *dangerousGetCPU(TR::Node *node, TR::CodeGenerator *cg)
   {
   return NULL;
   }
#endif

static TR::Register *inlineFixedTrg1Src1(TR::Node *node, TR::InstOpCode::Mnemonic op, TR::CodeGenerator *cg)
   {
   TR_ASSERT(node->getNumChildren() == 1, "Wrong number of children in inlineFixedTrg1Src1");

   TR::Node *firstChild = node->getFirstChild();
   TR::Register *srcRegister = cg->evaluate(firstChild);
   TR::Register *targetRegister = cg->allocateRegister();

   generateTrg1Src1Instruction(cg, op, node, targetRegister, srcRegister);

   node->setRegister(targetRegister);
   cg->decReferenceCount(firstChild);

   return targetRegister;
   }

static TR::Register *inlineLongNumberOfTrailingZeros(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR_ASSERT(node->getNumChildren() == 1, "Wrong number of children in inlineLongNumberOfTrailingZeros");

   TR::Node *firstChild = node->getFirstChild();
   TR::Register *srcRegister = cg->evaluate(firstChild);
   TR::Register *targetRegister = cg->allocateRegister();
   TR::Register *tempRegister = cg->allocateRegister();
   TR::Register *maskRegister = cg->allocateRegister();

   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addic, node, tempRegister, srcRegister->getLowOrder(), -1);
   generateTrg1Src1Instruction(cg, TR::InstOpCode::addme, node, targetRegister, srcRegister->getHighOrder());
   generateTrg1Src2Instruction(cg, TR::InstOpCode::andc, node, tempRegister, tempRegister, srcRegister->getLowOrder());
   generateTrg1Src2Instruction(cg, TR::InstOpCode::andc, node, targetRegister, targetRegister, srcRegister->getHighOrder());
   generateTrg1Src1Instruction(cg, TR::InstOpCode::cntlzw, node, targetRegister, targetRegister);
   generateTrg1Src1Instruction(cg, TR::InstOpCode::cntlzw, node, tempRegister, tempRegister);
   generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, maskRegister, targetRegister, 27, 0x1);
   generateTrg1Src1Instruction(cg, TR::InstOpCode::neg, node, maskRegister, maskRegister);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::AND, node, tempRegister, tempRegister, maskRegister);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, targetRegister, targetRegister, tempRegister);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::subfic, node, targetRegister, targetRegister, 64);

   cg->stopUsingRegister(tempRegister);
   cg->stopUsingRegister(maskRegister);

   node->setRegister(targetRegister);
   cg->decReferenceCount(firstChild);

   return targetRegister;
   }

static TR::Register *inlineIsAssignableFrom(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *)(comp->fe());
   TR::Node        *receiverNode = node->getFirstChild();
   TR::Node        *parmNode = node->getSecondChild();

   // If the receiver class is known at compile-time and the receiver and parm classes aren't the same,
   // we can check if the receiver is a super class of the parm class
   // Look for:
   // icall java/lang/Class.isAssignableFrom(Ljava/lang/Class;)Z
   //   aloadi <javaLangClassFromClass>
   //      loadaddr receiverj9class
   int32_t receiverClassDepth = -1;
   if (receiverNode->getOpCodeValue() == TR::aloadi &&
       receiverNode->getSymbolReference() == comp->getSymRefTab()->findJavaLangClassFromClassSymbolRef() &&
       receiverNode->getFirstChild()->getOpCodeValue() == TR::loadaddr)
      {
      TR::Node            *receiverJ9ClassNode = receiverNode->getFirstChild();
      TR::SymbolReference *receiverJ9ClassSymRef = receiverJ9ClassNode->getSymbolReference();
      if (receiverJ9ClassSymRef && !receiverJ9ClassSymRef->isUnresolved())
         {
         TR::StaticSymbol *receiverJ9ClassSym = receiverJ9ClassSymRef->getSymbol()->getStaticSymbol();
         if (receiverJ9ClassSym)
            {
            TR_OpaqueClassBlock *receiverJ9ClassPtr = (TR_OpaqueClassBlock *)receiverJ9ClassSym->getStaticAddress();
            if (receiverJ9ClassPtr)
               {
               receiverClassDepth = (int32_t)TR::Compiler->cls.classDepthOf(receiverJ9ClassPtr);
               if (receiverClassDepth >= 0)
                  {
                  static bool disable = feGetEnv("TR_disableInlineIsAssignableFromSuperTest") != NULL;
                  if (disable)
                     receiverClassDepth = -1;
                  }
               }
            }
         }
      }

   TR_PPCScratchRegisterManager *srm = cg->generateScratchRegisterManager();
   TR::Register                *receiverReg = cg->evaluate(node->getFirstChild());
   TR::Register                *parmReg = cg->evaluate(node->getSecondChild());
   TR::Register                *resultReg = cg->allocateRegister();
   TR::LabelSymbol                *outlinedCallLabel = generateLabelSymbol(cg);
   TR::LabelSymbol                *returnTrueLabel = generateLabelSymbol(cg);
   TR::LabelSymbol                *doneLabel = generateLabelSymbol(cg);

   doneLabel->setEndInternalControlFlow();

   // We don't want this guy to be live across the call to isAssignableFrom in the outlined section
   // because the CR reg will have to be spilled/restored.
   // Instead, we allocate it outside the srm and don't bother putting it in the dependencies on doneLabel.
   // For correctness we could free all CRs in the post conditions of doneLabel, but currently CRs are
   // never live outside of the evaluator they were created in so it's not a concern.
   TR::Register                *condReg = cg->allocateRegister(TR_CCR);

   // This is fine since we don't use any scratch regs after writing to the result reg
   srm->donateScratchRegister(resultReg);

   // If either the receiver or the parm is null, bail out
   TR::Register *nullTestReg = srm->findOrCreateScratchRegister();
   if (!receiverNode->isNonNull())
      {
      genNullTest(node, receiverReg, condReg, nullTestReg, NULL, cg);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, outlinedCallLabel, condReg);
      }
   if (!parmNode->isNonNull())
      {
      genNullTest(node, parmReg, condReg, nullTestReg, NULL, cg);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, outlinedCallLabel, condReg);
      }
   srm->reclaimScratchRegister(nullTestReg);

   // Compare receiver and parm
   generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, condReg, receiverReg, parmReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, returnTrueLabel, condReg);

   // Compare receiver and parm classes
   TR::Register *receiverJ9ClassReg = srm->findOrCreateScratchRegister();
   TR::Register *parmJ9ClassReg = srm->findOrCreateScratchRegister();
   generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, receiverJ9ClassReg,
                              TR::MemoryReference::createWithDisplacement(cg, receiverReg, fej9->getOffsetOfClassFromJavaLangClassField(), TR::Compiler->om.sizeofReferenceAddress()));
   generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, parmJ9ClassReg,
                              TR::MemoryReference::createWithDisplacement(cg, parmReg, fej9->getOffsetOfClassFromJavaLangClassField(), TR::Compiler->om.sizeofReferenceAddress()));
   generateTrg1Src2Instruction(cg,TR::InstOpCode::Op_cmpl, node, condReg, receiverJ9ClassReg, parmJ9ClassReg);

   TR::Register *scratch1Reg = NULL;
   if (receiverClassDepth >= 0)
      {
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, returnTrueLabel, condReg);

      scratch1Reg = srm->findOrCreateScratchRegister();
      TR::Register *scratch2Reg = srm->findOrCreateScratchRegister();

      genTestIsSuper(node, parmJ9ClassReg, receiverJ9ClassReg, condReg, scratch1Reg, scratch2Reg, receiverClassDepth, outlinedCallLabel, NULL, cg);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, outlinedCallLabel, condReg);

      srm->reclaimScratchRegister(scratch1Reg);
      srm->reclaimScratchRegister(scratch2Reg);
      }
   else
      {
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, outlinedCallLabel, condReg);
      }

   srm->reclaimScratchRegister(receiverJ9ClassReg);
   srm->reclaimScratchRegister(parmJ9ClassReg);
   generateLabelInstruction(cg, TR::InstOpCode::label, node, returnTrueLabel);
   generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, 1);

   TR::RegisterDependencyConditions *deps = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, 2 + srm->numAvailableRegisters(), cg->trMemory());
   deps->addPostCondition(receiverReg, TR::RealRegister::NoReg, UsesDependentRegister | ExcludeGPR0InAssigner);
   deps->addPostCondition(parmReg, TR::RealRegister::NoReg, UsesDependentRegister | ExcludeGPR0InAssigner);
   srm->addScratchRegistersToDependencyList(deps);
   // Make sure these two (added to the deps by the srm) have !gr0, since we use them as base regs
   deps->setPostDependencyExcludeGPR0(receiverJ9ClassReg);
   if (scratch1Reg)
      deps->setPostDependencyExcludeGPR0(scratch1Reg);
   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel, deps);

   TR_PPCOutOfLineCodeSection *outlinedHelperCall = new (cg->trHeapMemory()) TR_PPCOutOfLineCodeSection(node, TR::icall, resultReg, outlinedCallLabel, doneLabel, cg);
   cg->getPPCOutOfLineCodeSectionList().push_front(outlinedHelperCall);

   node->setRegister(resultReg);
   cg->decReferenceCount(node->getFirstChild());
   cg->decReferenceCount(node->getSecondChild());
   srm->stopUsingRegisters();
   cg->stopUsingRegister(condReg);

   return resultReg;
   }

static TR::Register*
hashCodeHelper(TR::Node *node, TR::CodeGenerator *cg, TR::DataType elementType,
               TR::Node *hashNode, bool isSigned)
   {
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *)(comp->fe());
   bool isLE = comp->target().cpu.isLittleEndian();
   const bool nonZeroInitial = hashNode != NULL && !hashNode->isConstZeroValue();
   int elementSize = TR::DataType::getSize(elementType);

   TR::Node *valueNode = node->getFirstChild();
   TR::Node *offsetNode = node->getSecondChild();
   TR::Node *countNode = node->getThirdChild();

   TR::Register *valueReg = cg->gprClobberEvaluate(valueNode);
   TR::Register *endReg = cg->gprClobberEvaluate(offsetNode);
   TR::Register *tempReg = cg->gprClobberEvaluate(countNode);
   TR::Register *multiplierAddrReg = cg->allocateRegister();
   TR::Register *condReg = cg->allocateRegister(TR_CCR);

   TR::Register *multiplierReg = cg->allocateRegister(TR_VRF);
   TR::Register *vtmp1Reg = cg->allocateRegister(TR_VRF);
   TR::Register *vtmp2Reg = cg->allocateRegister(TR_VRF);
   TR::Register *vconstant0Reg = cg->allocateRegister(TR_VRF);

   // we can load 16 bytes at once, but each register can only accumulate 4 values
   // so for smaller datatypes we need more than one register
   TR::Register *low4Reg = cg->allocateRegister(TR_VRF);
   TR::Register *high4Reg = NULL;
   TR::Register *third4Reg = NULL;
   TR::Register *fourth4Reg = NULL;
   TR::Register *vtmp3Reg = NULL;
   TR::Register *vunpackMaskReg = NULL;
   switch (elementType)
      {
      case TR::Int8:
         vtmp3Reg = cg->allocateRegister(TR_VRF); // a third register for unpacking
         third4Reg = cg->allocateRegister(TR_VRF);
         fourth4Reg = cg->allocateRegister(TR_VRF);
         // fall through
      case TR::Int16:
         high4Reg = cg->allocateRegister(TR_VRF);
         if (!isSigned)
            vunpackMaskReg = cg->allocateRegister(TR_VRF); // for masking unsigned types
         // fall through
      case TR::Int32:
         break;
      default:
         TR_ASSERT_FATAL(false, "Unsupported hashCodeHelper elementType");
      }

   // the hashReg is zero initially, unless specified otherwise
   TR::Register *hashReg = NULL;
   if (nonZeroInitial)
      {
      hashReg = cg->gprClobberEvaluate(hashNode);
      }
   else
      {
      hashReg = cg->allocateRegister();
      generateTrg1Src2Instruction(cg, TR::InstOpCode::XOR, node, hashReg, hashReg, hashReg);
      }

   TR::LabelSymbol *special1Label = generateLabelSymbol(cg); // for the length-1 special case
   TR::LabelSymbol *special2Label = generateLabelSymbol(cg); // for the length-2 special case
   TR::LabelSymbol *special3Label = generateLabelSymbol(cg); // for the length-3 special case
   TR::LabelSymbol *VSXLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *vectorLoopLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *serialLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *serialUnrollLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *endLabel = generateLabelSymbol(cg);

   // Skip header of the array
#if defined(J9VM_GC_SPARSE_HEAP_ALLOCATION)
   if (TR::Compiler->om.isOffHeapAllocationEnabled())
      {
      generateTrg1MemInstruction(
         cg, TR::InstOpCode::Op_load, node, valueReg,
         TR::MemoryReference::createWithDisplacement(
            cg, valueReg, TR::Compiler->om.offsetOfContiguousDataAddrField(),
            comp->target().is64Bit() ? 8 : 4)
         );
      }
   else
#endif /* J9VM_GC_SPARSE_HEAP_ALLOCATION */
      {
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, valueReg, valueReg,
                                     TR::Compiler->om.contiguousArrayHeaderSizeInBytes());
      }

   // v = v + offset<<lg(elementSize)
   // end = v + count<<lg(elementSize)
   switch (elementType)
      {
      case TR::Int8:
         if (comp->target().is64Bit()) // no need to clear garbage bits in 32-bit
            {
            generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldic, node, endReg, endReg, 0,
                                          CONSTANT64(0x00000000FFFFFFFF));
            generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldic, node, tempReg, tempReg, 0,
                                          CONSTANT64(0x00000000FFFFFFFF));
            }
         break;
      case TR::Int16:
         generateTrg1Src1Imm2Instruction(cg,
            comp->target().is64Bit() ? TR::InstOpCode::rldic : TR::InstOpCode::rlwinm,
            node, endReg, endReg, 1,
            comp->target().is64Bit() ? CONSTANT64(0x00000001FFFFFFFE) : 0xFFFFFFFE);
         generateTrg1Src1Imm2Instruction(cg,
            comp->target().is64Bit() ? TR::InstOpCode::rldic : TR::InstOpCode::rlwinm,
            node, tempReg, tempReg, 1,
            comp->target().is64Bit() ? CONSTANT64(0x00000001FFFFFFFE) : 0xFFFFFFFE);
         break;
      case TR::Int32:
         generateTrg1Src1Imm2Instruction(cg,
            comp->target().is64Bit() ? TR::InstOpCode::rldic : TR::InstOpCode::rlwinm,
            node, endReg, endReg, 2,
            comp->target().is64Bit() ? CONSTANT64(0x00000003FFFFFFFC) : 0xFFFFFFFC);
         generateTrg1Src1Imm2Instruction(cg,
            comp->target().is64Bit() ? TR::InstOpCode::rldic : TR::InstOpCode::rlwinm,
            node, tempReg, tempReg, 2,
            comp->target().is64Bit() ? CONSTANT64(0x00000003FFFFFFFC) : 0xFFFFFFFC);
         break;
      default:
         TR_ASSERT_FATAL(false, "Unsupported hashCodeHelper elementType");
      }
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, valueReg, valueReg, endReg);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, endReg, valueReg, tempReg);

   // use the vector loop for 16 bytes or more
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, condReg, tempReg, 16);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bge, node, VSXLabel, condReg);

   // serialLabel:
   generateLabelInstruction(cg, TR::InstOpCode::label, node, serialLabel);

   if (elementType != TR::Int32) // for Int32 there could only be 3 items at most
      {
      // we have specialised code for cases with 3 or fewer elements
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, condReg, tempReg, 3*elementSize);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::ble, node, special2Label, condReg);

      const int unrollFactor = 4, lgUnroll = 2;
      // get the number of times we are going the unroll loop
      // once we reach here, we will no longer need endReg, so we use it as our temp
      switch (elementType)
         {
         case TR::Int8:
            generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::srawi, node, endReg, tempReg, lgUnroll);
            // leave only the last two bits in tempReg
            generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, tempReg, tempReg, 0, 0x3);
            break;
         case TR::Int16:
            generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::srawi, node, endReg, tempReg, 1 + lgUnroll);
            // leave only the last three bits in tempReg
            generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, tempReg, tempReg, 0, 0x7);
            break;
         default:
            TR_ASSERT_FATAL(false, "Unsupported hashCodeHelper elementType");
         }
      // ctr = temp
      generateSrc1Instruction(cg, TR::InstOpCode::mtctr, node, endReg);

//   generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, hashReg, endReg);
//   generateLabelInstruction(cg, TR::InstOpCode::b, node, endLabel);
      // --- unrolled loop: do it at least once since we must have 4 or more items
      generateLabelInstruction(cg, TR::InstOpCode::label, node, serialUnrollLabel);
      // temp = hash << 5
      // hash = temp - hash
      // temp = v[i]
      // hash = hash + temp
      for (int i = 0; i < unrollFactor; i++)
         {
         generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, endReg, hashReg, 5, 0xFFFFFFFFFFFFFFE0);
         generateTrg1Src2Instruction(cg, TR::InstOpCode::subf, node, hashReg, hashReg, endReg);
         switch (elementType)
            {
            case TR::Int8:
               generateTrg1MemInstruction(cg, TR::InstOpCode::lbz, node, endReg,
                     TR::MemoryReference::createWithDisplacement(cg, valueReg, i, 1));
               if (isSigned)
                  {
                  generateTrg1Src1Instruction(cg, TR::InstOpCode::extsb, node, endReg, endReg);
                  }
               break;
            case TR::Int16:
               generateTrg1MemInstruction(cg, isSigned ? TR::InstOpCode::lha : TR::InstOpCode::lhz, node, endReg,
                     TR::MemoryReference::createWithDisplacement(cg, valueReg, i*2, 2));
               break;
            default:
               TR_ASSERT_FATAL(false, "Unsupported hashCodeHelper elementType");
            }
         generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, hashReg, hashReg, endReg);
         }
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, valueReg, valueReg, elementSize * unrollFactor);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bdnz, node, serialUnrollLabel, condReg);
      }

   // special cases
   generateLabelInstruction(cg, TR::InstOpCode::label, node, special2Label);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, condReg, tempReg, 2*elementSize);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, special3Label, condReg);
   // length 2
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::mulli, node, hashReg, hashReg, 961);
   switch (elementType)
      {
      case TR::Int8:
         generateTrg1MemInstruction(cg, TR::InstOpCode::lbzx, node, tempReg,
               TR::MemoryReference::createWithIndexReg(cg, NULL, valueReg, 1));
         if (isSigned)
            {
            generateTrg1Src1Instruction(cg, TR::InstOpCode::extsb, node, tempReg, tempReg);
            }
         break;
      case TR::Int16:
         generateTrg1MemInstruction(cg, isSigned ? TR::InstOpCode::lhax : TR::InstOpCode::lhzx, node, tempReg,
               TR::MemoryReference::createWithIndexReg(cg, NULL, valueReg, 2));
         break;
      case TR::Int32:
         generateTrg1MemInstruction(cg, TR::InstOpCode::lwzx, node, tempReg,
               TR::MemoryReference::createWithIndexReg(cg, NULL, valueReg, 4));
         break;
      default:
         TR_ASSERT_FATAL(false, "Unsupported hashCodeHelper elementType");
      }
   // shift then subtract is slightly faster than mul 31; multiplierAddrReg should be free now
   generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, multiplierAddrReg, tempReg,
         5, 0xFFFFFFFFFFFFFFE0);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::subf, node, tempReg, tempReg, multiplierAddrReg);
   switch (elementType)
      {
      case TR::Int8:
         generateTrg1MemInstruction(cg, TR::InstOpCode::lbz, node, endReg,
               TR::MemoryReference::createWithDisplacement(cg, valueReg, 1, 1));
         if (isSigned)
            {
            generateTrg1Src1Instruction(cg, TR::InstOpCode::extsb, node, endReg, endReg);
            }
         break;
      case TR::Int16:
         generateTrg1MemInstruction(cg, isSigned ? TR::InstOpCode::lha : TR::InstOpCode::lhz, node, endReg,
               TR::MemoryReference::createWithDisplacement(cg, valueReg, 2, 2));
         break;
      case TR::Int32:
         generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, endReg,
               TR::MemoryReference::createWithDisplacement(cg, valueReg, 4, 4));
         break;
      default:
         TR_ASSERT_FATAL(false, "Unsupported hashCodeHelper elementType");
      }
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, hashReg, hashReg, tempReg);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, hashReg, hashReg, endReg);
   generateLabelInstruction(cg, TR::InstOpCode::b, node, endLabel);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, special3Label);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, condReg, tempReg, 1*elementSize);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, special1Label, condReg);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, condReg, tempReg, 0);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, endLabel, condReg);
   // length 3
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::mulli, node, hashReg, hashReg, 29791);
   switch (elementType)
      {
      case TR::Int8:
         generateTrg1MemInstruction(cg, TR::InstOpCode::lbzx, node, tempReg,
               TR::MemoryReference::createWithIndexReg(cg, NULL, valueReg, 1));
         if (isSigned)
            {
            generateTrg1Src1Instruction(cg, TR::InstOpCode::extsb, node, tempReg, tempReg);
            }
         break;
      case TR::Int16:
         generateTrg1MemInstruction(cg, isSigned ? TR::InstOpCode::lhax : TR::InstOpCode::lhzx, node, tempReg,
               TR::MemoryReference::createWithIndexReg(cg, NULL, valueReg, 2));
         break;
      case TR::Int32:
         generateTrg1MemInstruction(cg, TR::InstOpCode::lwzx, node, tempReg,
               TR::MemoryReference::createWithIndexReg(cg, NULL, valueReg, 4));
         break;
      default:
         TR_ASSERT_FATAL(false, "Unsupported hashCodeHelper elementType");
      }
   // multiplierAddrReg should be free now
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::mulli, node, multiplierAddrReg, tempReg, 961);
   switch (elementType)
      {
      case TR::Int8:
         generateTrg1MemInstruction(cg, TR::InstOpCode::lbz, node, tempReg,
               TR::MemoryReference::createWithDisplacement(cg, valueReg, 1, 1));
         if (isSigned)
            {
            generateTrg1Src1Instruction(cg, TR::InstOpCode::extsb, node, tempReg, tempReg);
            }
         break;
      case TR::Int16:
         generateTrg1MemInstruction(cg, isSigned ? TR::InstOpCode::lha : TR::InstOpCode::lhz, node, tempReg,
               TR::MemoryReference::createWithDisplacement(cg, valueReg, 2, 2));
         break;
      case TR::Int32:
         generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, tempReg,
               TR::MemoryReference::createWithDisplacement(cg, valueReg, 4, 4));
         break;
      default:
         TR_ASSERT_FATAL(false, "Unsupported hashCodeHelper elementType");
      }
   // shift then subtract is slightly faster than mul 31
   generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, endReg, tempReg, 5, 0xFFFFFFFFFFFFFFE0);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::subf, node, tempReg, tempReg, endReg);
   switch (elementType)
      {
      case TR::Int8:
         generateTrg1MemInstruction(cg, TR::InstOpCode::lbz, node, endReg,
               TR::MemoryReference::createWithDisplacement(cg, valueReg, 2, 1));
         if (isSigned)
            {
            generateTrg1Src1Instruction(cg, TR::InstOpCode::extsb, node, endReg, endReg);
            }
         break;
      case TR::Int16:
         generateTrg1MemInstruction(cg, isSigned ? TR::InstOpCode::lha : TR::InstOpCode::lhz, node, endReg,
               TR::MemoryReference::createWithDisplacement(cg, valueReg, 4, 2));
         break;
      case TR::Int32:
         generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, endReg,
               TR::MemoryReference::createWithDisplacement(cg, valueReg, 8, 4));
         break;
      default:
         TR_ASSERT_FATAL(false, "Unsupported hashCodeHelper elementType");
      }
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, hashReg, hashReg, multiplierAddrReg);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, hashReg, hashReg, tempReg);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, hashReg, hashReg, endReg);
   generateLabelInstruction(cg, TR::InstOpCode::b, node, endLabel);

   // length 1
   generateLabelInstruction(cg, TR::InstOpCode::label, node, special1Label);
   // shift then subtract is slightly faster than mul 31
   generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, tempReg, hashReg, 5, 0xFFFFFFFFFFFFFFE0);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::subf, node, hashReg, hashReg, tempReg);
   switch (elementType)
      {
      case TR::Int8:
         generateTrg1MemInstruction(cg, TR::InstOpCode::lbzx, node, tempReg,
               TR::MemoryReference::createWithIndexReg(cg, NULL, valueReg, 1));
         if (isSigned)
            {
            generateTrg1Src1Instruction(cg, TR::InstOpCode::extsb, node, tempReg, tempReg);
            }
         break;
      case TR::Int16:
         generateTrg1MemInstruction(cg, isSigned ? TR::InstOpCode::lhax : TR::InstOpCode::lhzx, node, tempReg,
               TR::MemoryReference::createWithIndexReg(cg, NULL, valueReg, 2));
         break;
      case TR::Int32:
         generateTrg1MemInstruction(cg, TR::InstOpCode::lwzx, node, tempReg,
               TR::MemoryReference::createWithIndexReg(cg, NULL, valueReg, 4));
         break;
      default:
         TR_ASSERT_FATAL(false, "Unsupported hashCodeHelper elementType");
      }
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, hashReg, hashReg, tempReg);
   generateLabelInstruction(cg, TR::InstOpCode::b, node, endLabel);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, VSXLabel);
   // load multiplier (anything with more than 4 bytes can be truncated)
   // 31^0-6 = 1 31 961 29791 923521 28629151 887503681
   // 31^7 = 667E12CDF           31^8 = C694446F01         31^9 = 180BF449711F 31^10 = 2E97294E4B2C1
   // 31^11 = 5A44E007B1A55F     31^12 = AEE5720EE830681     31^13 = 152DC8CFCE1DDC99F
   // 31^14 = 2908B5129F59DB6A41 31^15 = 4F80DED414BE191DDDF 31^16 = 9A09AFBAE83050A9DE01
   static uint32_t multiplierVectors8[20] = {0x50A9DE01, 0x50A9DE01, 0x50A9DE01, 0x50A9DE01,
      0xE191DDDF, 0x59DB6A41, 0xE1DDC99F, 0xEE830681, 0x07B1A55F, 0x94E4B2C1, 0xF449711F, 0x94446F01,
      0x67E12CDF, 887503681, 28629151, 923521, 29791, 961, 31, 1};
   static uint32_t multiplierVectors16[12] = {0x94446F01, 0x94446F01, 0x94446F01, 0x94446F01,
      0x67E12CDF, 887503681, 28629151, 923521, 29791, 961, 31, 1};
   static uint32_t multiplierVectors32[8] = {923521, 923521, 923521, 923521,
                                               29791, 961, 31, 1};
   static uint32_t *multiplierPtr8 = NULL;
   static uint32_t *multiplierPtr16 = NULL;
   static uint32_t *multiplierPtr32 = NULL;
   switch (elementType)
      {
      case TR::Int8:
         if (!multiplierPtr8)
            {
            multiplierPtr8 = (uint32_t*) cg->allocateCodeMemory(20*sizeof(uint32_t),
               cg->getCurrentEvaluationBlock()->isCold());
            memcpy((void *) multiplierPtr8, (void *) multiplierVectors8, 20*sizeof(uint32_t));
            }
         // point to the beginning of the array
         loadAddressConstant(cg, false, node, (intptr_t) multiplierPtr8, multiplierAddrReg);
         break;
      case TR::Int16:
         if (!multiplierPtr16)
            {
            multiplierPtr16 = (uint32_t*) cg->allocateCodeMemory(12*sizeof(uint32_t),
               cg->getCurrentEvaluationBlock()->isCold());
            memcpy((void *) multiplierPtr16, (void *) multiplierVectors16, 12*sizeof(uint32_t));
            }
         // point to the beginning of the array
         loadAddressConstant(cg, false, node, (intptr_t) multiplierPtr16, multiplierAddrReg);
         break;
      case TR::Int32:
         if (!multiplierPtr32)
            {
            multiplierPtr32 = (uint32_t*) cg->allocateCodeMemory(8*sizeof(uint32_t),
               cg->getCurrentEvaluationBlock()->isCold());
            memcpy((void *) multiplierPtr32, (void *) multiplierVectors32, 8*sizeof(uint32_t));
            }
         // point to the beginning of the array
         loadAddressConstant(cg, false, node, (intptr_t) multiplierPtr32, multiplierAddrReg);
         break;
      default:
         TR_ASSERT_FATAL(false, "Unsupported hashCodeHelper elementType");
      }
   // load the multiplierReg
   generateTrg1MemInstruction(cg, TR::InstOpCode::lxvw4x, node, multiplierReg,
      TR::MemoryReference::createWithIndexReg(cg, NULL, multiplierAddrReg, 16));

   // clear accumulator registers
   switch (elementType)
      {
      case TR::Int8:
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vxor, node, third4Reg, third4Reg, third4Reg);
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vxor, node, fourth4Reg, fourth4Reg, fourth4Reg);
         //fall through
      case TR::Int16:
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vxor, node, high4Reg, high4Reg, high4Reg);
         //fall through
      case TR::Int32:
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vxor, node, low4Reg, low4Reg, low4Reg);
         break;
      default:
         TR_ASSERT_FATAL(false, "Unsupported hashCodeHelper elementType");
      }
   // ready all zeros and ones registers
   generateTrg1Src2Instruction(cg, TR::InstOpCode::vxor, node, vconstant0Reg, vconstant0Reg, vconstant0Reg);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::vnor, node, vtmp1Reg, vconstant0Reg, vconstant0Reg);
   // all four words in vunpackMaskReg are masks for the length of elementType
   if (elementType != TR::Int32 && !isSigned)
      {
      generateTrg1Src2ImmInstruction(cg, TR::InstOpCode::vsldoi, node, vunpackMaskReg, vconstant0Reg, vtmp1Reg, elementSize);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::vspltw, node, vunpackMaskReg, vunpackMaskReg, 3);
      }

   // load the initial value into the lowest word of the lowest register
   if (nonZeroInitial)
      {
      generateTrg1Src1Instruction(cg, TR::InstOpCode::mtvsrwz, node, low4Reg, hashReg);
      generateTrg1Src2ImmInstruction(cg, TR::InstOpCode::vsldoi, node, low4Reg, vconstant0Reg, low4Reg, 8);
      }

   // temp = end - 16 + elementSize which deal with all data we can load in batches
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, tempReg, endReg,
                                  elementSize - 16);

   // vectorLoop
   generateLabelInstruction(cg, TR::InstOpCode::label, node, vectorLoopLabel);

   // for each high/low register
   // unpack v to vtmp2Reg
   // accumulator = accumulator * 31^n + vtmp2Reg
   switch (elementType)
      {
      case TR::Int8:
         // load v
         if (comp->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P9))
            {
            generateTrg1MemInstruction(cg, TR::InstOpCode::lxvb16x, node, vtmp1Reg,
               TR::MemoryReference::createWithIndexReg(cg, NULL, valueReg, 16));
            }
         else // for P8 we only have lxvw4x
            {
            generateTrg1MemInstruction(cg, TR::InstOpCode::lxvw4x, node, vtmp1Reg,
               TR::MemoryReference::createWithIndexReg(cg, NULL, valueReg, 16));
            if (isLE)
               {
               // swap around the shorts in each word; we need 2 instructions to load 16
               generateTrg1ImmInstruction(cg, TR::InstOpCode::vspltisw, node, vtmp2Reg, 8);
               generateTrg1Src2Instruction(cg, TR::InstOpCode::vadduwm, node, vtmp2Reg, vtmp2Reg, vtmp2Reg);
               generateTrg1Src2Instruction(cg, TR::InstOpCode::vrlw, node, vtmp1Reg, vtmp1Reg, vtmp2Reg);
               // then swap around the bytes in each short
               generateTrg1ImmInstruction(cg, TR::InstOpCode::vspltish, node, vtmp2Reg, 8);
               generateTrg1Src2Instruction(cg, TR::InstOpCode::vrlh, node, vtmp1Reg, vtmp1Reg, vtmp2Reg);
               }
            }

         // unpack
         generateTrg1Src1Instruction(cg, TR::InstOpCode::vupkhsb, node, vtmp2Reg, vtmp1Reg);
         // fourth4
         generateTrg1Src1Instruction(cg, TR::InstOpCode::vupkhsh, node, vtmp3Reg, vtmp2Reg);
         if (!isSigned)
            {
            generateTrg1Src2Instruction(cg, TR::InstOpCode::vand, node, vtmp3Reg, vtmp3Reg, vunpackMaskReg);
            }
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vmuluwm, node, fourth4Reg, fourth4Reg, multiplierReg);
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vadduwm, node, fourth4Reg, fourth4Reg, vtmp3Reg);
         // third4
         generateTrg1Src1Instruction(cg, TR::InstOpCode::vupklsh, node, vtmp3Reg, vtmp2Reg);
         if (!isSigned)
            {
            generateTrg1Src2Instruction(cg, TR::InstOpCode::vand, node, vtmp3Reg, vtmp3Reg, vunpackMaskReg);
            }
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vmuluwm, node, third4Reg, third4Reg, multiplierReg);
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vadduwm, node, third4Reg, third4Reg, vtmp3Reg);

         generateTrg1Src1Instruction(cg, TR::InstOpCode::vupklsb, node, vtmp2Reg, vtmp1Reg);
         // high4
         generateTrg1Src1Instruction(cg, TR::InstOpCode::vupkhsh, node, vtmp3Reg, vtmp2Reg);
         if (!isSigned)
            {
            generateTrg1Src2Instruction(cg, TR::InstOpCode::vand, node, vtmp3Reg, vtmp3Reg, vunpackMaskReg);
            }
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vmuluwm, node, high4Reg, high4Reg, multiplierReg);
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vadduwm, node, high4Reg, high4Reg, vtmp3Reg);
         // low4
         generateTrg1Src1Instruction(cg, TR::InstOpCode::vupklsh, node, vtmp3Reg, vtmp2Reg);
         if (!isSigned)
            {
            generateTrg1Src2Instruction(cg, TR::InstOpCode::vand, node, vtmp3Reg, vtmp3Reg, vunpackMaskReg);
            }
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vmuluwm, node, low4Reg, low4Reg, multiplierReg);
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vadduwm, node, low4Reg, low4Reg, vtmp3Reg);
         break;
      case TR::Int16:
         // load v
         if (comp->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P9))
            {
            generateTrg1MemInstruction(cg, TR::InstOpCode::lxvh8x, node, vtmp1Reg,
               TR::MemoryReference::createWithIndexReg(cg, NULL, valueReg, 16));
            }
         else // for P8 we only have lxvw4x
            {
            generateTrg1MemInstruction(cg, TR::InstOpCode::lxvw4x, node, vtmp1Reg,
               TR::MemoryReference::createWithIndexReg(cg, NULL, valueReg, 16));
            if (isLE)
               {
               // swap around the shorts in each word; we need 2 instructions to load 16
               generateTrg1ImmInstruction(cg, TR::InstOpCode::vspltisw, node, vtmp2Reg, 8);
               generateTrg1Src2Instruction(cg, TR::InstOpCode::vadduwm, node, vtmp2Reg, vtmp2Reg, vtmp2Reg);
               generateTrg1Src2Instruction(cg, TR::InstOpCode::vrlw, node, vtmp1Reg, vtmp1Reg, vtmp2Reg);
               }
            }

         // high4
         generateTrg1Src1Instruction(cg, TR::InstOpCode::vupkhsh, node, vtmp2Reg, vtmp1Reg);
         if (!isSigned)
            {
            generateTrg1Src2Instruction(cg, TR::InstOpCode::vand, node, vtmp2Reg, vtmp2Reg, vunpackMaskReg);
            }
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vmuluwm, node, high4Reg, high4Reg, multiplierReg);
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vadduwm, node, high4Reg, high4Reg, vtmp2Reg);
         // low4
         generateTrg1Src1Instruction(cg, TR::InstOpCode::vupklsh, node, vtmp2Reg, vtmp1Reg);
         if (!isSigned)
            {
            generateTrg1Src2Instruction(cg, TR::InstOpCode::vand, node, vtmp2Reg, vtmp2Reg, vunpackMaskReg);
            }
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vmuluwm, node, low4Reg, low4Reg, multiplierReg);
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vadduwm, node, low4Reg, low4Reg, vtmp2Reg);
         break;
      case TR::Int32:
         generateTrg1MemInstruction(cg, TR::InstOpCode::lxvw4x, node, vtmp1Reg,
            TR::MemoryReference::createWithIndexReg(cg, NULL, valueReg, 16));
         // no need to unpack
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vmuluwm, node, low4Reg, low4Reg, multiplierReg);
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vadduwm, node, low4Reg, low4Reg, vtmp1Reg);
         break;
      default:
         TR_ASSERT_FATAL(false, "Unsupported hashCodeHelper elementType");
      }

   // v = v + 0x10
   // if v < temp goto VSX_LOOP
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, valueReg, valueReg, 0x10);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmpl8, node, condReg, valueReg, tempReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, vectorLoopLabel, condReg);

   // now that we don't need tempReg, use it to store the number of remaining bytes
   generateTrg1Src2Instruction(cg, TR::InstOpCode::subf, node, tempReg, valueReg, endReg);

   // shift the high/low registers
   switch (elementType)
      {
      case TR::Int8:
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, multiplierAddrReg, multiplierAddrReg, 0x10);
         generateTrg1MemInstruction(cg, TR::InstOpCode::lxvw4x, node, multiplierReg,
               TR::MemoryReference::createWithIndexReg(cg, NULL, multiplierAddrReg, 16));
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vmuluwm, node, fourth4Reg, fourth4Reg, multiplierReg);
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, multiplierAddrReg, multiplierAddrReg, 0x10);
         generateTrg1MemInstruction(cg, TR::InstOpCode::lxvw4x, node, multiplierReg,
               TR::MemoryReference::createWithIndexReg(cg, NULL, multiplierAddrReg, 16));
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vmuluwm, node, third4Reg, third4Reg, multiplierReg);
         // fall through
      case TR::Int16:
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, multiplierAddrReg, multiplierAddrReg, 0x10);
         generateTrg1MemInstruction(cg, TR::InstOpCode::lxvw4x, node, multiplierReg,
               TR::MemoryReference::createWithIndexReg(cg, NULL, multiplierAddrReg, 16));
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vmuluwm, node, high4Reg, high4Reg, multiplierReg);
         // fall through
      case TR::Int32:
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, multiplierAddrReg, multiplierAddrReg, 0x10);
         generateTrg1MemInstruction(cg, TR::InstOpCode::lxvw4x, node, multiplierReg,
               TR::MemoryReference::createWithIndexReg(cg, NULL, multiplierAddrReg, 16));
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vmuluwm, node, low4Reg, low4Reg, multiplierReg);
         break;
      default:
         TR_ASSERT_FATAL(false, "Unsupported hashCodeHelper elementType");
      }

   // sum the registers
   switch (elementType)
      {
      case TR::Int8:
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vadduwm, node, low4Reg, low4Reg, third4Reg);
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vadduwm, node, low4Reg, low4Reg, fourth4Reg);
         // fall through
      case TR::Int16:
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vadduwm, node, low4Reg, low4Reg, high4Reg);
      case TR::Int32:
         break;
      default:
         TR_ASSERT_FATAL(false, "Unsupported hashCodeHelper elementType");
      }
   generateTrg1Src2ImmInstruction(cg, TR::InstOpCode::vsldoi, node, vtmp1Reg, vconstant0Reg, low4Reg, 8);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::vadduwm, node, low4Reg, low4Reg, vtmp1Reg);
   generateTrg1Src2ImmInstruction(cg, TR::InstOpCode::vsldoi, node, vtmp1Reg, vconstant0Reg, low4Reg, 12);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::vadduwm, node, low4Reg, low4Reg, vtmp1Reg);

   // move result to hashReg
   generateTrg1Src2ImmInstruction(cg, TR::InstOpCode::vsldoi, node, vtmp1Reg, low4Reg, vconstant0Reg, 8);
   generateTrg1Src1Instruction(cg, TR::InstOpCode::mfvsrwz, node, hashReg, vtmp1Reg);

   generateLabelInstruction(cg, TR::InstOpCode::b, node, serialLabel);

   // End of this method
   TR::RegisterDependencyConditions *dependencies =
      new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0,
         (TR::Int8 == elementType ? 15 : (TR::Int16 == elementType ? 12 : 11)) + // accumulators
            (!isSigned && TR::Int32 != elementType), // vunpackMaskReg
         cg->trMemory());

   dependencies->addPostCondition(valueReg, TR::RealRegister::NoReg);
   dependencies->getPostConditions()->getRegisterDependency(0)->setExcludeGPR0(); // valueReg

   dependencies->addPostCondition(endReg, TR::RealRegister::NoReg);
   dependencies->getPostConditions()->getRegisterDependency(1)->setExcludeGPR0(); // endReg

   dependencies->addPostCondition(hashReg, TR::RealRegister::NoReg);
   dependencies->addPostCondition(tempReg, TR::RealRegister::NoReg);
   dependencies->addPostCondition(condReg, TR::RealRegister::NoReg);

   dependencies->addPostCondition(multiplierAddrReg, TR::RealRegister::NoReg);
   dependencies->getPostConditions()->getRegisterDependency(5)->setExcludeGPR0(); // multiplierAddrReg

   dependencies->addPostCondition(multiplierReg, TR::RealRegister::NoReg);
   dependencies->addPostCondition(vtmp1Reg, TR::RealRegister::NoReg);
   dependencies->addPostCondition(vtmp2Reg, TR::RealRegister::NoReg);
   dependencies->addPostCondition(vconstant0Reg, TR::RealRegister::NoReg);

   dependencies->addPostCondition(low4Reg, TR::RealRegister::NoReg);
   switch (elementType)
      {
      case TR::Int8:
         dependencies->addPostCondition(vtmp3Reg, TR::RealRegister::NoReg);
         dependencies->addPostCondition(third4Reg, TR::RealRegister::NoReg);
         dependencies->addPostCondition(fourth4Reg, TR::RealRegister::NoReg);
         // fall through
      case TR::Int16:
         dependencies->addPostCondition(high4Reg, TR::RealRegister::NoReg);
         if (!isSigned)
            dependencies->addPostCondition(vunpackMaskReg, TR::RealRegister::NoReg);
         // fall through
      case TR::Int32:
         break;
      default:
         TR_ASSERT_FATAL(false, "Unsupported hashCodeHelper elementType");
      }

   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, endLabel, dependencies);

   node->setRegister(hashReg);
   cg->decReferenceCount(valueNode);
   cg->decReferenceCount(offsetNode);
   cg->decReferenceCount(countNode);
   if (hashNode)
      cg->decReferenceCount(hashNode);

   cg->stopUsingRegister(valueReg);
   cg->stopUsingRegister(endReg);
   cg->stopUsingRegister(tempReg);
   cg->stopUsingRegister(condReg);
   cg->stopUsingRegister(multiplierAddrReg);

   cg->stopUsingRegister(multiplierReg);
   cg->stopUsingRegister(vtmp1Reg);
   cg->stopUsingRegister(vtmp2Reg);
   cg->stopUsingRegister(vconstant0Reg);

   cg->stopUsingRegister(low4Reg);
   switch (elementType)
      {
      case TR::Int8:
         cg->stopUsingRegister(vtmp3Reg);
         cg->stopUsingRegister(third4Reg);
         cg->stopUsingRegister(fourth4Reg);
         // fall through
      case TR::Int16:
         cg->stopUsingRegister(high4Reg);
         if (!isSigned)
            cg->stopUsingRegister(vunpackMaskReg);
         // fall through
      case TR::Int32:
         break;
      default:
         TR_ASSERT_FATAL(false, "Unsupported hashCodeHelper elementType");
      }

   return hashReg;
   }

static TR::Register*
inlineStringHashCode(TR::Node *node, TR::CodeGenerator *cg, bool isCompressed)
   {
   return hashCodeHelper(node, cg, isCompressed ? TR::Int8 : TR::Int16, NULL, false);
   }

static TR::Register*
inlineVectorizedHashCode(TR::Node* node, TR::CodeGenerator* cg)
   {
   TR::Register* hashReg = NULL;

   switch (node->getChild(4)->getConstValue())
      {
      // The following constants come from the values for the
      // type operand of the NEWARRAY instruction
      // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html#jvms-6.5.newarray.
      case 4: // T_BOOLEAN
         hashReg = hashCodeHelper(node, cg, TR::Int8, node->getChild(3), false);
         break;
      case 8: // T_BYTE
         hashReg = hashCodeHelper(node, cg, TR::Int8, node->getChild(3), true);
         break;
      case 5: // T_CHAR
         hashReg = hashCodeHelper(node, cg, TR::Int16, node->getChild(3), false);
         break;
      case 9: // T_SHORT
         hashReg = hashCodeHelper(node, cg, TR::Int16, node->getChild(3), true);
         break;
      case 10: // T_INT
         hashReg = hashCodeHelper(node, cg, TR::Int32, node->getChild(3), true);
         break;
      }
   if (hashReg != NULL)
      cg->decReferenceCount(node->getChild(4));

   return hashReg;
   }

static TR::Register *inlineEncodeUTF16(TR::Node *node, TR::CodeGenerator *cg)
   {
   // tree looks like:
   // icall com.ibm.jit.JITHelpers.encodeUtf16{Big,Little}()
   // input ptr
   // output ptr
   // input length (in elements)
   // Number of elements converted returned

   TR::MethodSymbol *symbol = node->getSymbol()->castToMethodSymbol();
   bool bigEndian = symbol->getRecognizedMethod() == TR::com_ibm_jit_JITHelpers_transformedEncodeUTF16Big;

   // Set up register dependencies
   const int gprClobberCount = 5;
   const int fprClobberCount = 4;
   const int vrClobberCount = 6;
   const int crClobberCount = 2;
   const int totalDeps = crClobberCount + gprClobberCount + fprClobberCount + vrClobberCount + 3;
   TR::RegisterDependencyConditions *deps = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(1, totalDeps, cg->trMemory());

   TR::Register *inputReg = cg->gprClobberEvaluate(node->getChild(0));
   TR::Register *outputReg = cg->gprClobberEvaluate(node->getChild(1));
   TR::Register *inputLenReg = cg->gprClobberEvaluate(node->getChild(2));
   TR::Register *outputLenReg = cg->allocateRegister();

   // Allocate clobbered registers
   TR::Register *gprClobbers[gprClobberCount], *fprClobbers[fprClobberCount], *vrClobbers[vrClobberCount], *crClobbers[crClobberCount];
   for (int i = 0; i < gprClobberCount; ++i) gprClobbers[i] = cg->allocateRegister(TR_GPR);
   for (int i = 0; i < fprClobberCount; ++i) fprClobbers[i] = cg->allocateRegister(TR_FPR);
   for (int i = 0; i < vrClobberCount; ++i) vrClobbers[i] = cg->allocateRegister(TR_VRF);
   for (int i = 0; i < crClobberCount; ++i) crClobbers[i] = cg->allocateRegister(TR_CCR);

   // Add the pre and post conditions
   // Input and output registers
   deps->addPreCondition(inputReg, TR::RealRegister::gr3);

   deps->addPostCondition(outputLenReg, TR::RealRegister::gr3);
   deps->addPostCondition(outputReg, TR::RealRegister::gr4);
   deps->addPostCondition(inputLenReg, TR::RealRegister::gr5);

   //CCR.
   deps->addPostCondition(crClobbers[0], TR::RealRegister::cr0);
   deps->addPostCondition(crClobbers[1], TR::RealRegister::cr6);

   //GPRs + Trampoline
   deps->addPostCondition(gprClobbers[0], TR::RealRegister::gr6);
   deps->addPostCondition(gprClobbers[1], TR::RealRegister::gr7);
   deps->addPostCondition(gprClobbers[2], TR::RealRegister::gr8);
   deps->addPostCondition(gprClobbers[3], TR::RealRegister::gr9);
   deps->addPostCondition(gprClobbers[4], TR::RealRegister::gr11);

   //VR's
   deps->addPostCondition(vrClobbers[0], TR::RealRegister::vr0);
   deps->addPostCondition(vrClobbers[1], TR::RealRegister::vr1);
   deps->addPostCondition(vrClobbers[2], TR::RealRegister::vr2);
   deps->addPostCondition(vrClobbers[3], TR::RealRegister::vr3);
   deps->addPostCondition(vrClobbers[4], TR::RealRegister::vr4);
   deps->addPostCondition(vrClobbers[5], TR::RealRegister::vr5);

   //FP/VSR
   deps->addPostCondition(fprClobbers[0], TR::RealRegister::fp0);
   deps->addPostCondition(fprClobbers[1], TR::RealRegister::fp1);
   deps->addPostCondition(fprClobbers[2], TR::RealRegister::fp2);
   deps->addPostCondition(fprClobbers[3], TR::RealRegister::fp3);

   // Generate helper call
   TR_RuntimeHelper helper;
   helper = bigEndian ? TR_PPCencodeUTF16Big : TR_PPCencodeUTF16Little;
   TR::SymbolReference *helperSym = cg->comp()->getSymRefTab()->findOrCreateRuntimeHelper(helper);
   generateDepImmSymInstruction(cg, TR::InstOpCode::bl, node, (uintptr_t)helperSym->getMethodAddress(), deps, helperSym);

   for (uint32_t i = 0; i < node->getNumChildren(); ++i) cg->decReferenceCount(node->getChild(i));

   // Spill the clobbered registers
   if (inputReg != node->getChild(0)->getRegister()) cg->stopUsingRegister(inputReg);
   if (outputReg != node->getChild(1)->getRegister()) cg->stopUsingRegister(outputReg);
   if (inputLenReg != node->getChild(2)->getRegister()) cg->stopUsingRegister(inputLenReg);
   for (int i = 0; i < gprClobberCount; ++i) cg->stopUsingRegister(gprClobbers[i]);
   for (int i = 0; i < vrClobberCount; ++i) cg->stopUsingRegister(vrClobbers[i]);
   for (int i = 0; i < fprClobberCount; ++i) cg->stopUsingRegister(fprClobbers[i]);
   for (int i = 0; i < crClobberCount; ++i) cg->stopUsingRegister(crClobbers[i]);

   cg->machine()->setLinkRegisterKilled(true);
   cg->setHasCall();
   node->setRegister(outputLenReg);

   return outputLenReg;
   }

static TR::Register *inlineIntrinsicIndexOf_P10(TR::Node *node, TR::CodeGenerator *cg, bool isLatin1)
   {
   static bool disableIndexOfStringIntrinsic = feGetEnv("TR_DisableIndexOfStringIntrinsic") != NULL;
   if (disableIndexOfStringIntrinsic)
      return nullptr;
   TR::Compilation *comp = cg->comp();
   auto vectorCompareOp = isLatin1 ? TR::InstOpCode::vcmpequb_r : TR::InstOpCode::vcmpequh_r;
   TR::InstOpCode::Mnemonic scalarLoadOp = isLatin1 ? TR::InstOpCode::lbzx : TR::InstOpCode::lhzx;

   // This evaluator function handles different indexOf() intrinsics, some of which are static calls without a
   // receiver. Hence, the need for static call check.
   const bool isStaticCall = node->getSymbolReference()->getSymbol()->castToMethodSymbol()->isStatic();
   const uint8_t firstCallArgIdx = isStaticCall ? 0 : 1;
   TR::Register *array = cg->evaluate(node->getChild(firstCallArgIdx));
   TR::Register *ch = cg->evaluate(node->getChild(firstCallArgIdx+1));
   TR::Register *offset = cg->evaluate(node->getChild(firstCallArgIdx+2));
   TR::Register *length = cg->gprClobberEvaluate(node->getChild(firstCallArgIdx+3));

   TR::LabelSymbol *startLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *mainLoop = generateLabelSymbol(cg);
   TR::LabelSymbol *residueLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *resultLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *endLabel = generateLabelSymbol(cg);

   TR::Register *cr6 = cg->allocateRegister(TR_CCR);

   TR::Register *position = cg->allocateRegister();
   TR::Register *endPos = cg->allocateRegister();
   TR::Register *arrAddress = cg->allocateRegister();
   TR::Register *result = cg->allocateRegister();
   TR::Register *temp = cg->allocateRegister();
   TR::Register *vec0 = cg->allocateRegister(TR_VRF);
   TR::Register *vec1 = cg->allocateRegister(TR_VRF);
   TR::Register *vec2 = cg->allocateRegister(TR_VRF);

   startLabel->setStartInternalControlFlow();
   endLabel->setEndInternalControlFlow();

   generateLabelInstruction(cg, TR::InstOpCode::label, node, startLabel);

   generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, result, -1);
   // check empty
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp4, node, cr6, offset, length);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bge, node, endLabel, cr6);

   generateTrg1Src1Instruction(cg, TR::InstOpCode::extsw, node, position, offset);
   generateTrg1Src1Instruction(cg, TR::InstOpCode::extsw, node, endPos, length);

   /*
    * Zero out upper bits. ch is later compared against values loaded by lbzx.
    * Comparison expect bits other than the lowest 8 bits are zero.
    */
   if (isLatin1)
      {
      generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, ch, ch, 0, 0xFF);
      }


   if (!isLatin1)
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, position, position, position);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, endPos, endPos, endPos);
      }

   /*
    * Determine the address of the first byte to read either by loading from dataAddr or adding the header size.
    * This is followed by adding in the offset.
    */
#if defined(J9VM_GC_SPARSE_HEAP_ALLOCATION)
   if (TR::Compiler->om.isOffHeapAllocationEnabled())
      {
      generateTrg1MemInstruction(
         cg, TR::InstOpCode::ld, node, arrAddress,
         TR::MemoryReference::createWithDisplacement(
            cg, array, TR::Compiler->om.offsetOfContiguousDataAddrField(), 8)
         );
      }
   else
#endif /* J9VM_GC_SPARSE_HEAP_ALLOCATION */
      {
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, arrAddress, array,
                                     TR::Compiler->om.contiguousArrayHeaderSizeInBytes());
      }

   // match first byte
   generateTrg1MemInstruction(cg, scalarLoadOp, node, temp, TR::MemoryReference::createWithIndexReg(cg, position, arrAddress, isLatin1 ? 1 : 2));
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp4, node, cr6, temp, ch);
   generateTrg1Src3Instruction(cg, TR::InstOpCode::iseleq, node, result, offset, result, cr6);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, endLabel, cr6);

   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, position, position, isLatin1 ? 1 : 2);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp4, node, cr6, position, endPos);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, endLabel, cr6);

   generateTrg1Src1Instruction(cg, TR::InstOpCode::mtvsrwz, node, vec1, ch);
   if (isLatin1)
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::vspltb, node, vec1, vec1, 7);
   else
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::vsplth, node, vec1, vec1, 3);


   generateTrg1Src2Instruction(cg, TR::InstOpCode::subf, node, temp, position, endPos);
   generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, endPos, temp, 0, 0xF);
   generateShiftRightLogicalImmediate(cg, node, temp, temp, 4);

   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, cr6, temp, 0);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, residueLabel, cr6);

   generateSrc1Instruction(cg, TR::InstOpCode::mtctr, node, temp);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, mainLoop);      // mainloop
   if (!isLatin1)
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::lxvh8x, node, vec0, arrAddress, position);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::vcmpequh_r, node, vec0, vec0, vec1);
      }
   else
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::lxvb16x, node, vec0, arrAddress, position);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::vcmpequb_r, node, vec0, vec0, vec1);
      }
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, resultLabel, cr6);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, position, position, 16);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bdnz, node, mainLoop, cr6);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, residueLabel);

   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, cr6, endPos, 0);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, endLabel, cr6);

   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, arrAddress, arrAddress, position);
   generateShiftLeftImmediateLong(cg, node, endPos, endPos, 56);

   generateTrg1Src2Instruction(cg, TR::InstOpCode::lxvll, node, vec0, arrAddress, endPos);


   if (comp->target().cpu.isLittleEndian() && !isLatin1)
      {
      generateTrg1ImmInstruction(cg, TR::InstOpCode::vspltisb, node, vec2, 8);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::vrlq, node, vec1, vec1, vec2);
      }

   if (isLatin1)
      generateTrg1Src2Instruction(cg, TR::InstOpCode::vcmpequb, node, vec0, vec0, vec1);
   else
      generateTrg1Src2Instruction(cg, TR::InstOpCode::vcmpequh, node, vec0, vec0, vec1);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, resultLabel);  // resultLabel
   generateTrg1Src1Instruction(cg, TR::InstOpCode::vclzlsbb, node, temp, vec0);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, temp, temp, position);

   if (!isLatin1)
      {
      generateShiftRightLogicalImmediate(cg, node, temp, temp, 1);
      }
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp4, node, cr6, temp, length);

   generateTrg1Src3Instruction(cg, TR::InstOpCode::isellt, node, result, temp, result, cr6);
   // end

   TR::RegisterDependencyConditions *deps = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, 13, cg->trMemory());

   deps->addPostCondition(array, TR::RealRegister::NoReg);
   deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();
   deps->addPostCondition(ch, TR::RealRegister::NoReg);
   deps->addPostCondition(offset, TR::RealRegister::NoReg);
   deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();
   deps->addPostCondition(length, TR::RealRegister::NoReg);

   deps->addPostCondition(cr6, TR::RealRegister::cr6);

   deps->addPostCondition(position, TR::RealRegister::NoReg);
   deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();
   deps->addPostCondition(endPos, TR::RealRegister::NoReg);
   deps->addPostCondition(arrAddress, TR::RealRegister::NoReg);
   deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();
   deps->addPostCondition(result, TR::RealRegister::NoReg);
   deps->addPostCondition(temp, TR::RealRegister::NoReg);
   deps->addPostCondition(vec0, TR::RealRegister::NoReg);
   deps->addPostCondition(vec1, TR::RealRegister::NoReg);
   deps->addPostCondition(vec2, TR::RealRegister::NoReg);

   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, endLabel, deps);

   deps->stopUsingDepRegs(cg, result);

   node->setRegister(result);

   if (!isStaticCall)
      {
      cg->recursivelyDecReferenceCount(node->getChild(0));
      }
   for (int32_t i = firstCallArgIdx; i < node->getNumChildren(); i++)
      {
      cg->decReferenceCount(node->getChild(i));
      }

   return result;
   }

static TR::Register *inlineIntrinsicIndexOf(TR::Node *node, TR::CodeGenerator *cg, bool isLatin1)
   {
   static bool disableIndexOfStringIntrinsic = feGetEnv("TR_DisableIndexOfStringIntrinsic") != NULL;
   if (disableIndexOfStringIntrinsic)
      return nullptr;
   TR::Compilation *comp = cg->comp();
   auto vectorCompareOp = isLatin1 ? TR::InstOpCode::vcmpequb_r : TR::InstOpCode::vcmpequh_r;
   auto scalarLoadOp = isLatin1 ? TR::InstOpCode::lbzx : TR::InstOpCode::lhzx;

   // This evaluator function handles different indexOf() intrinsics, some of which are static calls without a
   // receiver. Hence, the need for static call check.
   const bool isStaticCall = node->getSymbolReference()->getSymbol()->castToMethodSymbol()->isStatic();
   const uint8_t firstCallArgIdx = isStaticCall ? 0 : 1;
   TR::Register *array = cg->evaluate(node->getChild(firstCallArgIdx));
   TR::Register *ch = cg->evaluate(node->getChild(firstCallArgIdx+1));
   TR::Register *offset = cg->evaluate(node->getChild(firstCallArgIdx+2));
   TR::Register *length = cg->gprClobberEvaluate(node->getChild(firstCallArgIdx+3));

   TR::Register *cr0 = cg->allocateRegister(TR_CCR);
   TR::Register *cr6 = cg->allocateRegister(TR_CCR);

   TR::Register *zeroRegister = cg->allocateRegister();
   TR::Register *result = cg->allocateRegister();
   TR::Register *arrAddress = cg->allocateRegister();
   TR::Register *currentAddress = cg->allocateRegister();
   TR::Register *endAddress = cg->allocateRegister();

   TR::Register *targetVector = cg->allocateRegister(TR_VRF);
   TR::Register *targetVectorNot = cg->allocateRegister(TR_VRF);
   TR::Register *searchVector = cg->allocateRegister(TR_VRF);
   TR::Register *permuteVector = cg->allocateRegister(TR_VRF);

   TR_PPCScratchRegisterManager *srm = cg->generateScratchRegisterManager();

   TR::LabelSymbol *startLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *notSmallLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *vectorLoopLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *residueLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *notFoundLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *foundLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *foundExactLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *endLabel = generateLabelSymbol(cg);

   startLabel->setStartInternalControlFlow();
   endLabel->setEndInternalControlFlow();

   generateLabelInstruction(cg, TR::InstOpCode::label, node, startLabel);

   // Special case for empty strings, which always return -1
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp4, node, cr0, offset, length);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, notFoundLabel, cr0);

   // IMPORTANT: The upper 32 bits of a 64-bit register containing an int are undefined. Since the
   // indices are being passed in as ints, we must ensure that their upper 32 bits are not garbage.
   generateTrg1Src1Instruction(cg, TR::InstOpCode::extsw, node, result, offset);
   generateTrg1Src1Instruction(cg, TR::InstOpCode::extsw, node, endAddress, length);

   if (!isLatin1)
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, result, result, result);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, endAddress, endAddress, endAddress);
      }

   if (node->getChild(firstCallArgIdx+2)->getReferenceCount() == 1)
      srm->donateScratchRegister(offset);
   if (node->getChild(firstCallArgIdx+3)->getReferenceCount() == 1)
      srm->donateScratchRegister(length);

   /*
    * Determine the address of the first byte to read either by loading from dataAddr or adding the header size.
    * This is followed by adding in the offset.
    */
#if defined(J9VM_GC_SPARSE_HEAP_ALLOCATION)
   if (TR::Compiler->om.isOffHeapAllocationEnabled())
      {
      generateTrg1MemInstruction(
         cg, TR::InstOpCode::ld, node, arrAddress,
         TR::MemoryReference::createWithDisplacement(
            cg, array, TR::Compiler->om.offsetOfContiguousDataAddrField(), 8)
         );
      }
   else
#endif /* J9VM_GC_SPARSE_HEAP_ALLOCATION */
      {
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, arrAddress, array,
                                     TR::Compiler->om.contiguousArrayHeaderSizeInBytes());
      }

   if (node->getChild(firstCallArgIdx)->getReferenceCount() == 1)
      srm->donateScratchRegister(array);

   // Handle the first character using a simple scalar compare. Otherwise, first character matches
   // would be slower than the old scalar comparison loop. This is a problem since first character
   // matches are common in certain contexts, e.g. StringTokenizer where the default first character
   // to each String.indexOf call is ' ', which is the most common whitespace character.
      {
      TR::Register *value = srm->findOrCreateScratchRegister();
      TR::Register *zxTargetScalar = srm->findOrCreateScratchRegister();

      // Since we're going to do a load followed immediately by a comparison, we need to ensure that
      // the target scalar is zero-extended and *not* sign-extended.
      generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rlwinm, node, zxTargetScalar, ch, 0, isLatin1 ? 0xff : 0xffff);

      generateTrg1MemInstruction(cg, scalarLoadOp, node, value, TR::MemoryReference::createWithIndexReg(cg, result, arrAddress, isLatin1 ? 1 : 2));
      generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp4, node, cr0, value, zxTargetScalar);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, foundExactLabel, cr0);

      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, result, result, isLatin1 ? 1 : 2);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp8, node, cr0, result, endAddress);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, notFoundLabel, cr0);

      srm->reclaimScratchRegister(zxTargetScalar);
      srm->reclaimScratchRegister(value);
      }

   generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, zeroRegister, 0);

   // Calculate the actual addresses of the start and end points
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, endAddress, arrAddress, endAddress);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, currentAddress, arrAddress, result);

   // Splat the value to be compared against and its bitwise complement into two vector registers
   // for later use
   generateTrg1Src1Instruction(cg, TR::InstOpCode::mtvsrwz, node, targetVector, ch);
   if (node->getChild(firstCallArgIdx+1)->getReferenceCount() == 1)
      srm->donateScratchRegister(ch);
   if (isLatin1)
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::vspltb, node, targetVector, targetVector, 7);
   else
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::vsplth, node, targetVector, targetVector, 3);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::vnor, node, targetVectorNot, targetVector, targetVector);

   TR::Register *endVectorAddress = srm->findOrCreateScratchRegister();
   TR::Register *startVectorAddress = srm->findOrCreateScratchRegister();

   // Calculate the end address for what can be compared using full vector compares. After reaching
   // this address, the remaining comparisons (if required) will need special handling.
   generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldicr, node, endVectorAddress, endAddress, 0, CONSTANT64(0xfffffffffffffff0));

   // Check if the entire string is contained within a single 16-byte aligned section. If this
   // happens, we need to handle that case specially.
   generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldicr, node, startVectorAddress, currentAddress, 0, CONSTANT64(0xfffffffffffffff0));
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp8, node, cr0, startVectorAddress, endVectorAddress);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, notSmallLabel, cr0);

   // If we got here, then the entire string to search is contained within a single 16-byte aligned
   // vector and the end of the string is not aligned to the end of the vector. We don't know
   // whether the start of the string is aligned, but we'll assume it isn't since that just results
   // in a few unnecessary operations producing the correct answer regardless.

   // First, we read in an entire 16-byte aligned vector containing the entire string. Since this
   // load is done with 16-byte natural alignment, this load can't cross a page boundary and cause
   // an unexpected page fault. However, this will read some garbage at the start and end of the
   // vector. Assume that we read n bytes of garbage before the string and m bytes of garbage after
   // the string.
   generateTrg1MemInstruction(cg, TR::InstOpCode::lvx, node, searchVector, TR::MemoryReference::createWithIndexReg(cg, zeroRegister, currentAddress, 16));

      {
      TR::Register *scratchRegister = srm->findOrCreateScratchRegister();

      // We need to ensure that the garbage we read can't compare as equal to the target value for
      // obvious reasons. In order to accomplish this, we first rotate forwards by m bytes. This
      // places all garbage at the beginning of the vector.
      generateTrg1Src1Instruction(cg, TR::InstOpCode::neg, node, scratchRegister, endAddress);
      generateTrg1Src2Instruction(cg, comp->target().cpu.isLittleEndian() ? TR::InstOpCode::lvsl : TR::InstOpCode::lvsr, node, permuteVector, zeroRegister, scratchRegister);
      generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, searchVector, searchVector, searchVector, permuteVector);

      // Next, we shift the vector backwards by (n + m) bytes shifting in the bitwise complement of
      // the target value. This causes the garbage to end up at the vector register, having been
      // replaced with a bit pattern that can never compare as equal to the target value.
      generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, scratchRegister, scratchRegister, currentAddress);
      if (comp->target().cpu.isLittleEndian())
         {
         generateTrg1Src2Instruction(cg, TR::InstOpCode::lvsr, node, permuteVector, zeroRegister, scratchRegister);
         generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, searchVector, targetVectorNot, searchVector, permuteVector);
         }
      else
         {
         generateTrg1Src2Instruction(cg, TR::InstOpCode::lvsl, node, permuteVector, zeroRegister, scratchRegister);
         generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, searchVector, searchVector, targetVectorNot, permuteVector);
         }

      srm->reclaimScratchRegister(scratchRegister);

      // Now the search vector is ready for comparison: none of the garbage can compare as equal to
      // our target value and the start of the vector is now aligned to the start of the string. So
      // we can now perform the comparison as normal.
      generateTrg1Src2Instruction(cg, vectorCompareOp, node, searchVector, searchVector, targetVector);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, foundLabel, cr6);
      generateLabelInstruction(cg, TR::InstOpCode::b, node, notFoundLabel);
      }

   generateLabelInstruction(cg, TR::InstOpCode::label, node, notSmallLabel);

   // Check if we already have 16-byte alignment. Vector loads require 16-byte alignment, so if we
   // aren't properly aligned, we'll need to handle comparisons specially until we achieve 16-byte
   // alignment.
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp8, node, cr0, currentAddress, startVectorAddress);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, vectorLoopLabel, cr0);

   // We are not on a 16-byte boundary, so we cannot directly load the first 16 bytes of the string
   // for comparison. Instead, we load the 16 byte vector starting from the 16-byte aligned section
   // containing the start of the string. Since we have 16-byte natural alignment, this can't cause
   // an unexpected page fault.
   generateTrg1MemInstruction(cg, TR::InstOpCode::lvx, node, searchVector, TR::MemoryReference::createWithIndexReg(cg, zeroRegister, currentAddress, 16));

   // However, before we can run any comparisons on the loaded vector, we must ensure that the extra
   // garbage read before the start of the string can't match the target character. To do this, we
   // shift the loaded vector backwards by n bytes shifting in the bitwise complement of the target
   // character.
   if (comp->target().cpu.isLittleEndian())
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::lvsr, node, permuteVector, zeroRegister, currentAddress);
      generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, searchVector, targetVectorNot, searchVector, permuteVector);
      }
   else
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::lvsl, node, permuteVector, zeroRegister, currentAddress);
      generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, searchVector, searchVector, targetVectorNot, permuteVector);
      }

   // Now our vector is ready for comparison: no garbage can match the target value and the start of
   // the vector is now aligned to the start of the string.
   generateTrg1Src2Instruction(cg, vectorCompareOp, node, searchVector, searchVector, targetVector);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, foundLabel, cr6);

   // If the first vector didn't match, then we can slide right into the standard vectorized loop.
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, currentAddress, startVectorAddress, 0x10);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp8, node, cr0, currentAddress, endVectorAddress);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, residueLabel, cr0);

   srm->reclaimScratchRegister(startVectorAddress);

   // This is the heart of the vectorized loop, working just like any standard vectorized loop.
   generateLabelInstruction(cg, TR::InstOpCode::label, node, vectorLoopLabel);
   generateTrg1MemInstruction(cg, TR::InstOpCode::lvx, node, searchVector, TR::MemoryReference::createWithIndexReg(cg, zeroRegister, currentAddress, 16));
   generateTrg1Src2Instruction(cg, vectorCompareOp, node, searchVector, searchVector, targetVector);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, foundLabel, cr6);

   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, currentAddress, currentAddress, 0x10);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp8, node, cr0, currentAddress, endVectorAddress);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, vectorLoopLabel, cr0);

   srm->reclaimScratchRegister(endVectorAddress);

   // Now we're done with the part of the loop which can be handled as a normal vectorized loop. If
   // there are no more elements to compare, we're done. Otherwise, we need to handle the residue.
   generateLabelInstruction(cg, TR::InstOpCode::label, node, residueLabel);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp8, node, cr0, currentAddress, endAddress);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, notFoundLabel, cr0);

   // Usually, we would need a residue loop here, but it's safe to read beyond the end of the string
   // here. Since our load will have 16-byte natural alignment, it can't cross a page boundary and
   // cause an unexpected page fault.
   generateTrg1MemInstruction(cg, TR::InstOpCode::lvx, node, searchVector, TR::MemoryReference::createWithIndexReg(cg, zeroRegister, currentAddress, 16));

   TR::Register *shiftAmount = srm->findOrCreateScratchRegister();

   // Before we can run our comparison, we need to ensure that the garbage from beyond the end of
   // the string cannot compare as equal to our target value. To do this, we first rotate the vector
   // forwards by n bytes then shift back by n bytes, shifting in the bitwise complement of the
   // target value.
   generateTrg1Src1Instruction(cg, TR::InstOpCode::neg, node, shiftAmount, endAddress);
   if (comp->target().cpu.isLittleEndian())
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::lvsl, node, permuteVector, zeroRegister, shiftAmount);
      generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, searchVector, searchVector, searchVector, permuteVector);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::lvsr, node, permuteVector, zeroRegister, shiftAmount);
      generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, searchVector, targetVectorNot, searchVector, permuteVector);
      }
   else
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::lvsr, node, permuteVector, zeroRegister, shiftAmount);
      generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, searchVector, searchVector, searchVector, permuteVector);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::lvsl, node, permuteVector, zeroRegister, shiftAmount);
      generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, searchVector, searchVector, targetVectorNot, permuteVector);
      }

   srm->reclaimScratchRegister(shiftAmount);

   // Now we run our comparison as normal
   generateTrg1Src2Instruction(cg, vectorCompareOp, node, searchVector, searchVector, targetVector);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, foundLabel, cr6);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, notFoundLabel);

   // We've looked through the entire string and didn't find our target character, so return the
   // sentinel value -1
   generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, result, -1);
   generateLabelInstruction(cg, TR::InstOpCode::b, node, endLabel);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, foundLabel);

   // We've managed to find a match for the target value in the loaded vector, but we don't yet know
   // which element of the loaded vector is the first match. The comparison will have set matching
   // elements in the vector to -1 and non-matching elements to 0. We can find the first matching
   // element by gathering the first bit of every byte in the vector register...

   // Set permuteVector = 0x000102030405060708090a0b0c0d0e0f
   generateTrg1Src2Instruction(cg, TR::InstOpCode::lvsl, node, permuteVector, zeroRegister, zeroRegister);

   // For little-endian, reverse permuteVector so that we can find the first set bit using a count
   // leading zeroes test instead of a count trailing zeroes test. This is necessary since cnttzw
   // wasn't introduced until Power 9.
   if (comp->target().cpu.isLittleEndian())
      {
      generateTrg1ImmInstruction(cg, TR::InstOpCode::vspltisb, node, targetVector, 0x0f);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::vsububm, node, permuteVector, targetVector, permuteVector);
      }

   // Set permuteVector = 0x00081018202830384048505860687078 (reversed for LE)
   generateTrg1ImmInstruction(cg, TR::InstOpCode::vspltisb, node, targetVector, 3);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::vslb, node, permuteVector, permuteVector, targetVector);

   generateTrg1Src2Instruction(cg, TR::InstOpCode::vbpermq, node, searchVector, searchVector, permuteVector);
   generateTrg1Src1Instruction(cg, TR::InstOpCode::mfvsrwz, node, result, searchVector);

   // Then count the number of leading zeroes from the obtained result. This tells us the index (in
   // bytes) of the first matching element in the vector. Note that there is no way to count leading
   // zeroes of a half-word, so we count leading zeroes of a word and subtract 16 since the value
   // we're interested in is in the least significant half-word.
   generateTrg1Src1Instruction(cg, TR::InstOpCode::cntlzw, node, result, result);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, result, result, -16);

   // Finally, combine this with the address of the last vector load to find the address of the
   // first matching element in the string. Finally, use this to calculate the corresponding index.
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, result, result, currentAddress);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::subf, node, result, arrAddress, result);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, foundExactLabel);
   if (!isLatin1)
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::srawi, node, result, result, 1);

   TR::RegisterDependencyConditions *deps = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, 15 + srm->numAvailableRegisters(), cg->trMemory());

   if (node->getChild(firstCallArgIdx)->getReferenceCount() != 1)
      {
      deps->addPostCondition(array, TR::RealRegister::NoReg);
      deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();
      }
   if (node->getChild(firstCallArgIdx+1)->getReferenceCount() != 1)
      deps->addPostCondition(ch, TR::RealRegister::NoReg);
   if (node->getChild(firstCallArgIdx+2)->getReferenceCount() != 1)
      deps->addPostCondition(offset, TR::RealRegister::NoReg);
   if (node->getChild(firstCallArgIdx+3)->getReferenceCount() != 1)
      deps->addPostCondition(length, TR::RealRegister::NoReg);

   deps->addPostCondition(cr0, TR::RealRegister::cr0);
   deps->addPostCondition(cr6, TR::RealRegister::cr6);

   deps->addPostCondition(zeroRegister, TR::RealRegister::NoReg);
   deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();
   deps->addPostCondition(result, TR::RealRegister::NoReg);
   deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();
   deps->addPostCondition(arrAddress, TR::RealRegister::NoReg);
   deps->addPostCondition(currentAddress, TR::RealRegister::NoReg);
   deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();
   deps->addPostCondition(endAddress, TR::RealRegister::NoReg);

   deps->addPostCondition(targetVector, TR::RealRegister::NoReg);
   deps->addPostCondition(targetVectorNot, TR::RealRegister::NoReg);
   deps->addPostCondition(searchVector, TR::RealRegister::NoReg);
   deps->addPostCondition(permuteVector, TR::RealRegister::NoReg);

   srm->addScratchRegistersToDependencyList(deps, true);

   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, endLabel, deps);

   deps->stopUsingDepRegs(cg, result);

   node->setRegister(result);

   if (!isStaticCall)
      {
      cg->recursivelyDecReferenceCount(node->getChild(0));
      }
   for (int32_t i = firstCallArgIdx; i < node->getNumChildren(); i++)
      {
      cg->decReferenceCount(node->getChild(i));
      }

   return result;
   }

/**
 * This function generates a sequence of instructions to perform an array compare specialized for the String indexOf intrinsic.
 * The specialization means it skips checks that are expected to have already been done earlier in the String indexOf code.
 *
 * @param node                   TR::Node associated with the code being generated.
 * @param cg                     Code Generator.
 * @param currentAddressReg      Register that holds an input value for the code being generated. Value in the register is not modified.
                                 This is the address of the first character in the section of the main string being compared to see if it matches the full substring.
 * @param subStringAddressReg    Register that holds an input value for the code being generated. Value in the register is not modified.
                                 This is the address of the first character of the substring.
 * @param subStringLenReg        Register that holds an input value for the code being generated. Value in the register is not modified.
                                 This is the length of the substring in bytes.
 * @param tempReg                Register to hold temporary GPR values so it gets clobbered. Starting value is not read.
 * @param temp2Reg               Register to hold temporary GPR values so it gets clobbered. Starting value is not read.
 * @param subSearchOffsetReg     Register to hold the offset into the substring. This is used to keep track of how far the substring match has progresses.
                                 The register gets clobbered and its starting value is not read.
 * @param vec1Reg                Register to hold temporary vector values so it gets clobbered. Starting value is not read.
 * @param vec2Reg                Register to hold temporary vector values so it gets clobbered. Starting value is not read.
 * @param cr0Reg                 One of the condition registers used.
 * @param cr6Reg                 One of the condition registers used.
 * @param subStringMatchLabel    If a full match is found on the substring, the code will jump to this label.
 * @param subStringMismatchLabel If a mismatch between the substring and main string is found, jump to this label. If NULL, jump to the end of the code segment generated by performArrayCmp instead.
 */
static void performArrayCmp(TR::Node *node, TR::CodeGenerator *cg, TR::Register *currentAddressReg, TR::Register *subStringAddressReg, TR::Register *subStringLenReg, TR::Register *tempReg, TR::Register *temp2Reg, TR::Register *subSearchOffsetReg, TR::Register *vec1Reg, TR::Register *vec2Reg, TR::Register *cr0Reg, TR::Register *cr6Reg, TR::LabelSymbol *subStringMatchLabel, TR::LabelSymbol *subStringMismatchLabel)
   {
   /*
    * This is used by inlineIntrinsicIndexOfString to check if the remainder for a substring matches the next characters in the main string.
    * This process is very similar to an arraycmp but this version has special optimizations for the inlineIntrinsicIndexOfString case.
    * The most notable one is this arraycmp may read one fewer byte of data to avoid smaller loads and to avoid byte by byte processsing.
    * This works since this assembly is only run after it is confirmed that the first character of the substring matches a character in the main string.
    * This occurs whenever the substring has an odd number of bytes.
    *
    * | Substring Length | 2 byte load | 4 byte load | 8 byte load | 16 byte load |
    * | ---------------- | ----------- | ----------- | ----------- | ------------ |
    * |                1 |           0 |           0 |           0 |            0 | The one character in the substring is a known match so no further loads are needed.
    * |                2 |           1 |             |             |              |
    * |                3 |           1 |             |             |              | Recomparing the first byte isn't needed so a single 2 byte load handles the rest of the substring.
    * |                4 |             |           1 |             |              |
    * |                5 |             |           1 |             |              |
    * |                6 |           1 |           1 |             |              |
    * |                7 |           1 |           1 |             |              |
    * |                8 |             |             |           1 |              |
    * |                9 |             |             |           1 |              |
    * |               10 |           1 |             |           1 |              |
    * |               11 |           1 |             |           1 |              |
    * |               12 |             |           1 |           1 |              |
    * |               13 |             |           1 |           1 |              |
    * |               14 |           1 |           1 |           1 |              |
    * |               15 |           1 |           1 |           1 |              |
    * |               16 |             |             |             |            1 |
    * |               17 |             |             |             |            1 |
    */
   TR::LabelSymbol *load16LoopLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *skipLoad16Label = generateLabelSymbol(cg);
   TR::LabelSymbol *skipLoad8Label = generateLabelSymbol(cg);
   TR::LabelSymbol *skipLoad4Label = generateLabelSymbol(cg);

   /*
    * subStringMismatchLabel is the location to jump to if the substring does not match the next characters in the main string.
    * This label can be provided by the caller of performArrayCmp if there is a specific location to jump to.
    * If it isn't, createSubStringMismatchLabel is generated at the end of the assembly made by performArrayCmp.
    */
   bool createSubStringMismatchLabel = false;
   if (nullptr == subStringMismatchLabel)
      {
      subStringMismatchLabel = generateLabelSymbol(cg);
      createSubStringMismatchLabel = true;
      }

   /*
    * If subStringLenReg is even, tempReg is equal to the length of the substring.
    * If subStringLenReg is odd,  tempReg is now 1 less than the length of the substring.
    * This is done so one fewer byte is compared in odd cases.
    * tempReg holds the actual number of bytes that need to be compared and is always an even number.
    * If tempReg is less than 2, it must be 0 so a full substring match has been found.
    */
   generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldicr, node, tempReg, subStringLenReg, 0, CONSTANT64(0xfffffffffffffffe));
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpli8, node, cr6Reg, tempReg, 2);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, subStringMatchLabel, cr6Reg);

   /* If substring length is odd, the offset is adjusted by 1 to account for not needing to recompare the first byte. */
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, subSearchOffsetReg, subStringLenReg, cr0Reg, 1);

   /* tempReg is at least 2. If it is less than 4, jump to the section that loads 2 bytes from the array. */
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpli8, node, cr6Reg, tempReg, 4);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, skipLoad4Label, cr6Reg);

   /* tempReg is at least 4. If it is less than 8, jump to the section that loads 4 bytes from the array. */
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpli8, node, cr6Reg, tempReg, 8);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, skipLoad8Label, cr6Reg);

   /* tempReg is at least 8. If it is less than 16, jump to the section that loads 8 bytes from the array. */
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpli8, node, cr6Reg, tempReg, 16);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, skipLoad16Label, cr6Reg);

   /*
    * tempReg is at least 16 so we can use vector operations to compare the arrays.
    * The maximum number of iterations is put into the count register.
    */
   generateShiftRightLogicalImmediateLong(cg, node, tempReg, subStringLenReg, 4);
   generateSrc1Instruction(cg, TR::InstOpCode::mtctr, node, tempReg);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, load16LoopLabel);

   /*
    * This arraycmp only needs to know if there is a match or not. It doesn't need to know where the mismatch is.
    * So it doesn't matter if the byte order gets scrambled as long as it is scrambled the same way for both loaded values.
    */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::lxvw4x, node, vec1Reg, subSearchOffsetReg, currentAddressReg);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::lxvw4x, node, vec2Reg, subSearchOffsetReg, subStringAddressReg);

   /* If a difference is found in the 16 loaded bytes, jump to subStringMismatchLabel to indicate that the substring was not found. */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::vcmpequb_r, node, vec1Reg, vec1Reg, vec2Reg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bge, node, subStringMismatchLabel, cr6Reg);

   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi2, node, subSearchOffsetReg, subSearchOffsetReg, 16);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bdnz, node, load16LoopLabel, cr6Reg);

   /* If there are no more bytes, the arrays match each other. Jump to subStringMatchLabel to handle the substring match. */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::subf_r, node, tempReg, subSearchOffsetReg, subStringLenReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, subStringMatchLabel, cr0Reg);

   /* There are at least 2 bytes left. If there are less than 4 bytes left jump to the 2 byte load section. */
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpli8, node, cr6Reg, tempReg, 4);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, skipLoad4Label, cr6Reg);

   /* There are at least 4 bytes left. If there are less than 8 bytes left jump to the 4 byte load section. */
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpli8, node, cr6Reg, tempReg, 8);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, skipLoad8Label, cr6Reg);

   /* There are 8 to 15 bytes left at this point. */
   generateLabelInstruction(cg, TR::InstOpCode::label, node, skipLoad16Label);

   /* Use 8 byte loads to search for a mismatch between the two arrays. No loop is needed since there are less than 16 bytes left. */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::ldx, node, tempReg, subSearchOffsetReg, currentAddressReg);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::ldx, node, temp2Reg, subSearchOffsetReg, subStringAddressReg);
   /* If a mismatch is found, jump to subStringMismatchLabel to indicate that the substring was not found. */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp8, node, cr6Reg, tempReg, temp2Reg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, subStringMismatchLabel, cr6Reg);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi2, node, subSearchOffsetReg, subSearchOffsetReg, 8);

   /* If there are no more bytes, the arrays match each other. Jump to subStringMatchLabel to handle the substring match. */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::subf_r, node, tempReg, subSearchOffsetReg, subStringLenReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, subStringMatchLabel, cr0Reg);

   /* There are at least 2 bytes left. If there are less than 4 bytes left jump to the 2 byte load section. */
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpli8, node, cr6Reg, tempReg, 4);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, skipLoad4Label, cr6Reg);

   /* There are 4 to 7 bytes left at this point. */
   generateLabelInstruction(cg, TR::InstOpCode::label, node, skipLoad8Label);

   /*
    * Use 4 byte loads to search for a mismatch between the two arrays.
    * There are 4 to 7 bytes left so this only needs to be done once.
    */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::lwzx, node, tempReg, subSearchOffsetReg, currentAddressReg);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::lwzx, node, temp2Reg, subSearchOffsetReg, subStringAddressReg);
   /* If a mismatch is found, jump to subStringMismatchLabel to indicate that the substring was not found. */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp4, node, cr6Reg, tempReg, temp2Reg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, subStringMismatchLabel, cr6Reg);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi2, node, subSearchOffsetReg, subSearchOffsetReg, 4);

   /* If there are no more bytes, the arrays match each other. Jump to subStringMatchLabel to handle the substring match. */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::subf_r, node, tempReg, subSearchOffsetReg, subStringLenReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, subStringMatchLabel, cr0Reg);

   /* There are exactly 2 bytes left at this point. */
   generateLabelInstruction(cg, TR::InstOpCode::label, node, skipLoad4Label);

   generateTrg1Src2Instruction(cg, TR::InstOpCode::lhzx, node, tempReg, subSearchOffsetReg, currentAddressReg);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::lhzx, node, temp2Reg, subSearchOffsetReg, subStringAddressReg);
   /* If a match is found, jump to subStringMatchLabel to handle the substring match. */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp4, node, cr6Reg, tempReg, temp2Reg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, subStringMatchLabel, cr6Reg);
   /* A fallthrough means the loaded values did not match. Continue on to handle the case where the substring was not found. */

   if (createSubStringMismatchLabel)
      {
      generateLabelInstruction(cg, TR::InstOpCode::label, node, subStringMismatchLabel);
      }
   }

static TR::Register *inlineIntrinsicIndexOfString(TR::Node *node, TR::CodeGenerator *cg, bool isLatin1)
   {
   static bool disableStringIndexOfStringIntrinsic = feGetEnv("TR_DisableStringIndexOfStringIntrinsic") != NULL;
   if (disableStringIndexOfStringIntrinsic)
      return nullptr;

   /*
    * This intrinsic searches for a string inside of a string.
    * For clarity, the string being searched for is called the substring.
    * The string being search in is called the main string.
    */

   //TODO: Support for StringUTF16.indexOf is a WIP and currently unused. (isLatin1 is always true)

   TR::Compilation *comp = cg->comp();
   auto vectorCompareOp = isLatin1 ? TR::InstOpCode::vcmpequb_r : TR::InstOpCode::vcmpequh_r;
   auto scalarLoadOp = isLatin1 ? TR::InstOpCode::lbzx : TR::InstOpCode::lhzx;

   TR::Register *arrayObjReg     = cg->evaluate(node->getChild(0));
   TR::Register *arrayLenReg     = cg->evaluate(node->getChild(1));
   TR::Register *subStringObjReg = cg->evaluate(node->getChild(2));
   TR::Register *strCountReg     = cg->evaluate(node->getChild(3));
   TR::Register *fromIndexReg    = cg->evaluate(node->getChild(4));

   TR::Register *arrayAddressReg     = nullptr;
   TR::Register *endAddressReg       = nullptr;
   TR::Register *subStringAddressReg = nullptr;
   TR::Register *subStringLenReg     = nullptr;
   TR::Register *resultReg           = nullptr;

   int32_t numDependencies = 19;

   if (cg->canClobberNodesRegister(node->getChild(0)))
      {
      arrayAddressReg = arrayObjReg;
      }
   else
      {
      arrayAddressReg = cg->allocateRegister();
      numDependencies++;
      }

   if (cg->canClobberNodesRegister(node->getChild(1)))
      {
      endAddressReg = arrayLenReg;
      }
   else
      {
      endAddressReg = cg->allocateRegister();
      numDependencies++;
      }

   if (cg->canClobberNodesRegister(node->getChild(2)))
      {
      subStringAddressReg = subStringObjReg;
      }
   else
      {
      subStringAddressReg = cg->allocateRegister();
      numDependencies++;
      }

   if (cg->canClobberNodesRegister(node->getChild(3)))
      {
      subStringLenReg = strCountReg;
      }
   else
      {
      subStringLenReg = cg->allocateRegister();
      numDependencies++;
      }

   if (cg->canClobberNodesRegister(node->getChild(4)))
      {
      resultReg = fromIndexReg;
      }
   else
      {
      resultReg = cg->allocateRegister();
      numDependencies++;
      }

   TR::Register *currentAddressReg  = cg->allocateRegister();
   TR::Register *zeroReg            = cg->allocateRegister();
   TR::Register *tempReg            = cg->allocateRegister();
   TR::Register *temp2Reg           = cg->allocateRegister();
   TR::Register *subSearchOffsetReg = cg->allocateRegister();

   TR::Register *vec1Reg            = cg->allocateRegister(TR_VRF);
   TR::Register *vec2Reg            = cg->allocateRegister(TR_VRF);
   TR::Register *targetVectorReg    = cg->allocateRegister(TR_VRF);
   TR::Register *targetVectorNotReg = cg->allocateRegister(TR_VRF);
   TR::Register *searchVectorReg    = cg->allocateRegister(TR_VRF);
   TR::Register *permuteVectorReg   = cg->allocateRegister(TR_VRF);
   TR::Register *maskVectorReg      = cg->allocateRegister(TR_VRF);

   TR::Register *cr0Reg = cg->allocateRegister(TR_CCR);
   TR::Register *cr6Reg = cg->allocateRegister(TR_CCR);

   TR::LabelSymbol *startLabel                 = generateLabelSymbol(cg);
   TR::LabelSymbol *foundEarlyLabel            = generateLabelSymbol(cg);
   TR::LabelSymbol *notFoundEarlyLabel         = generateLabelSymbol(cg);
   TR::LabelSymbol *foundEndVectorLabel        = generateLabelSymbol(cg);
   TR::LabelSymbol *foundEndVectorRetryLabel   = generateLabelSymbol(cg);
   TR::LabelSymbol *notSmallLabel              = generateLabelSymbol(cg);
   TR::LabelSymbol *firstContinueLabel         = generateLabelSymbol(cg);
   TR::LabelSymbol *vectorLoopLabel            = generateLabelSymbol(cg);
   TR::LabelSymbol *vectorContinueLabel        = generateLabelSymbol(cg);
   TR::LabelSymbol *residueLabel               = generateLabelSymbol(cg);
   TR::LabelSymbol *notFoundLabel              = generateLabelSymbol(cg);
   TR::LabelSymbol *foundFirstVectorLabel      = generateLabelSymbol(cg);
   TR::LabelSymbol *foundFirstVectorRetryLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *foundVectorLabel           = generateLabelSymbol(cg);
   TR::LabelSymbol *foundVectorRetryLabel      = generateLabelSymbol(cg);
   TR::LabelSymbol *foundLabel                 = generateLabelSymbol(cg);
   TR::LabelSymbol *foundExactLabel            = generateLabelSymbol(cg);
   TR::LabelSymbol *endLabel                   = generateLabelSymbol(cg);

   startLabel->setStartInternalControlFlow();
   endLabel->setEndInternalControlFlow();


   generateLabelInstruction(cg, TR::InstOpCode::label, node, startLabel);

      {
      /* If offset + number of characters in the substring is greater than the number of characters in the main string it can't be found. In this case jump to notFoundLabel to return -1. */
      generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, tempReg, fromIndexReg, strCountReg);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::cmpl4, node, cr0Reg, tempReg, arrayLenReg);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bgt, node, notFoundLabel, cr0Reg);
      }

   /*
    * IMPORTANT: The upper 32 bits of a 64-bit register containing an int are undefined. Since the
    * indices are being passed in as ints, we must ensure that their upper 32 bits are not garbage.
    */
   generateTrg1Src1Instruction(cg, TR::InstOpCode::extsw, node, resultReg, fromIndexReg);
   generateTrg1Src1Instruction(cg, TR::InstOpCode::extsw, node, subStringLenReg, strCountReg);

   /* Under UTF16, the indices are doubled since offset and size in bytes are needed later. */
   if (!isLatin1)
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, resultReg, resultReg, resultReg);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, subStringLenReg, subStringLenReg, subStringLenReg);
      }

   /*
    * Determine the address of the first byte to read either by loading from dataAddr or adding the header size.
    * This is followed by adding in the offset.
    */
#if defined(J9VM_GC_SPARSE_HEAP_ALLOCATION)
   if (TR::Compiler->om.isOffHeapAllocationEnabled())
      {
      generateTrg1MemInstruction(
         cg, TR::InstOpCode::ld, node, arrayAddressReg,
         TR::MemoryReference::createWithDisplacement(
            cg, arrayObjReg, TR::Compiler->om.offsetOfContiguousDataAddrField(), 8)
         );
      generateTrg1MemInstruction(
         cg, TR::InstOpCode::ld, node, subStringAddressReg,
         TR::MemoryReference::createWithDisplacement(
            cg, subStringObjReg, TR::Compiler->om.offsetOfContiguousDataAddrField(), 8)
         );
      }
   else
#endif /* J9VM_GC_SPARSE_HEAP_ALLOCATION */
      {
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, arrayAddressReg, arrayObjReg,
                                     TR::Compiler->om.contiguousArrayHeaderSizeInBytes());
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, subStringAddressReg, subStringObjReg,
                                     TR::Compiler->om.contiguousArrayHeaderSizeInBytes());
      }

   /* resultReg currently holds the starting offset. currentAddressReg will hold the address of the first byte of the main string to look at. */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, currentAddressReg, arrayAddressReg, resultReg);

   /*
    * This is a early check to see if there is an immediate match for the substring in the main string.
    * The first character of the substring is compared against the first (after adjusted for offset) character of the main string.
    * If it matches, jump to foundEarlyLabel to try and match the rest of the substring. If it doesn't match, jump back to notFoundEarlyLabel.
    */
   generateTrg1MemInstruction(cg, isLatin1 ? TR::InstOpCode::lbz : TR::InstOpCode::lhz, node, tempReg, TR::MemoryReference::createWithDisplacement(cg, currentAddressReg, 0, isLatin1 ? 1 : 2));
   generateTrg1MemInstruction(cg, isLatin1 ? TR::InstOpCode::lbz : TR::InstOpCode::lhz, node, zeroReg, TR::MemoryReference::createWithDisplacement(cg, subStringAddressReg, 0, isLatin1 ? 1 : 2));
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp4, node, cr0Reg, tempReg, zeroReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, foundEarlyLabel, cr0Reg);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, notFoundEarlyLabel);

   /*
    * IMPORTANT: The upper 32 bits of a 64-bit register containing an int are undefined. Since the
    * indices are being passed in as ints, we must ensure that their upper 32 bits are not garbage.
    */
   generateTrg1Src1Instruction(cg, TR::InstOpCode::extsw, node, endAddressReg, arrayLenReg);

   /* Under UTF16, the indices are doubled since offset and size in bytes are needed later. */
   if (!isLatin1)
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, endAddressReg, endAddressReg, endAddressReg);
      }

   /*
    * It sometimes isn't necessary to match the first character of the substring against the last characters in the main string
    * because there aren't enough characters left in the main string to hold the rest of the substring.
    * A check for this case is performed here and goes to notFoundLabel to return -1 if there aren't enough characters left.
    */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::subf, node, endAddressReg, subStringLenReg, endAddressReg);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp8, node, cr0Reg, resultReg, endAddressReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bge, node, notFoundLabel, cr0Reg);

   /*
    * Calculate the actual addresses of the start and end points.
    * currentAddressReg is the address of the next character to load in the main string to compare against the substring.
    * endAddressReg is the address of the first character in the main string that does NOT need to be compared against the first character of the substring.
    * The reason for this is it is known there can not be a match at that point because there aren't enough characters left in the main string.
    *
    * eg. Searching for the string "abcd" inside the string "0123456789".
    *
    *   0123456789
    *          abcd
    *
    * endAddressReg will contain the address of the character "7". This is the point where a match for "abcd" is impossible since there are 3 characters left
    * and the substring is 4 characters long. When endAddressReg is reached, the search ends and -1 is returned.
    */
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, endAddressReg, endAddressReg, isLatin1 ? 1 : 2);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, currentAddressReg, currentAddressReg, isLatin1 ? 1 : 2);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, endAddressReg, arrayAddressReg, endAddressReg);

   /*
    * This section sets up values in registers that will be needed multiple times.
    * zeroReg currently holds the value of the first character of the substring.
    * targetVectorReg is a vector register with the first character of the substring splat across it. This is used to quickly search for first character matches later.
    * zeroReg is then set to 0.
    */
   generateTrg1Src1Instruction(cg, TR::InstOpCode::mtvsrwz, node, targetVectorReg, zeroReg);
   generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, zeroReg, 0);

   if (isLatin1)
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::vspltb, node, targetVectorReg, targetVectorReg, 7);
   else
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::vsplth, node, targetVectorReg, targetVectorReg, 3);

   /*
    * targetVectorNotReg is a vector register temporarily set to all 0s.
    * maskVectorReg is set to 0xFF for Latin1 and 0xFFFF for UTF16. This is later used to mask values in a vector register to prevent matching a second time.
    */
      {
      generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, tempReg, isLatin1 ? 1 : 2);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::vxor, node, targetVectorNotReg, targetVectorReg, targetVectorReg);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::vnor, node, vec1Reg, targetVectorNotReg, targetVectorNotReg);

      if (comp->target().cpu.isLittleEndian())
         {
         generateTrg1Src2Instruction(cg, TR::InstOpCode::lvsl, node, permuteVectorReg, zeroReg, tempReg);
         generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, maskVectorReg, targetVectorNotReg, vec1Reg, permuteVectorReg);
         }
      else
         {
         generateTrg1Src2Instruction(cg, TR::InstOpCode::lvsr, node, permuteVectorReg, zeroReg, tempReg);
         generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, maskVectorReg, vec1Reg, targetVectorNotReg, permuteVectorReg);
         }
      }

   /* targetVectorNotReg is set to the bitwise complement of targetVectorReg. These are values that will never match the first character of the substring. */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::vnor, node, targetVectorNotReg, targetVectorReg, targetVectorReg);

   /*
    * Calculate the end address for what can be compared using full vector compares.
    * After reaching this address, the remaining comparisons (if required) will need special handling.
    * A vector load from tempReg will contain 1-16 bytes that need to be compared against the first character of the substring.
    * A vector load from temp2Reg will contain 0-15 bytes that need to be compared against the first character of the substring.
    */
   generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldicr, node, tempReg, currentAddressReg, 0, CONSTANT64(0xfffffffffffffff0));
   generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldicr, node, temp2Reg, endAddressReg, 0, CONSTANT64(0xfffffffffffffff0));

   /*
    * Check if the entire string is contained within a single 16-byte aligned section. If this
    * happens, we need to handle that case specially.
    * If the value in tempReg is equal to temp2Reg, then there are 1-15 bytes that need to be looked at and they are all within
    * a 16 byte aligned section.
    */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp8, node, cr0Reg, tempReg, temp2Reg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, notSmallLabel, cr0Reg);

   /*
    * If we got here, then the entire string to search is contained within a single 16-byte aligned
    * vector and the end of the string is not aligned to the end of the vector. We don't know
    * whether the start of the string is aligned, but we'll assume it isn't since that just results
    * in a few unnecessary operations producing the correct answer regardless.
    *
    * First, we read in an entire 16-byte aligned vector containing the entire string. Since this
    * load is done with 16-byte natural alignment, this load can't cross a page boundary and cause
    * an unexpected page fault. However, this will read some garbage at the start and end of the
    * vector. Assume that we read n bytes of garbage before the string and m bytes of garbage after
    * the string.
    *
    * After the lvx.
    * Little Endian example:
    *   mm mm mm 09 08 07 06 05 04 03 02 01 nn nn nn nn
    *
    * Big Endian example:
    *   nn nn nn nn 01 02 03 04 05 06 07 08 09 mm mm mm
    */
   generateTrg1MemInstruction(cg, TR::InstOpCode::lvx, node, searchVectorReg, TR::MemoryReference::createWithIndexReg(cg, zeroReg, currentAddressReg, 16));

   /*
    * We need to ensure that the garbage we read can't compare as equal to the target value for
    * obvious reasons. In order to accomplish this, we first rotate forwards by m bytes. This
    * places all garbage at the beginning of the vector.
    *
    * Little Endian example:
    *   mm mm mm 09 08 07 06 05 04 03 02 01 nn nn nn nn -> 09 08 07 06 05 04 03 02 01 nn nn nn nn mm mm mm
    *
    * Big Endian example:
    *   nn nn nn nn 01 02 03 04 05 06 07 08 09 mm mm mm -> mm mm mm nn nn nn nn 01 02 03 04 05 06 07 08 09
    */
   generateTrg1Src1Instruction(cg, TR::InstOpCode::neg, node, tempReg, endAddressReg); // Only the low 4 bits in tempReg are used so it acts as a value between 0-15.
   generateTrg1Src2Instruction(cg, comp->target().cpu.isLittleEndian() ? TR::InstOpCode::lvsl : TR::InstOpCode::lvsr, node, permuteVectorReg, zeroReg, tempReg);
   generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, searchVectorReg, searchVectorReg, searchVectorReg, permuteVectorReg);

   /*
    * Next, we shift the vector backwards by (n + m) bytes shifting in the bitwise complement of
    * the target value. This causes the garbage to end up at the vector register, having been
    * replaced with a bit pattern that can never compare as equal to the target value.
    *
    * Little Endian example:
    *   09 08 07 06 05 04 03 02 01 nn nn nn nn mm mm mm -> XX XX XX XX XX XX XX 09 08 07 06 05 04 03 02 01
    *
    * Big Endian example:
    *   mm mm mm nn nn nn nn 01 02 03 04 05 06 07 08 09 -> 01 02 03 04 05 06 07 08 09 XX XX XX XX XX XX XX
    */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, tempReg, tempReg, currentAddressReg);
   if (comp->target().cpu.isLittleEndian())
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::lvsr, node, permuteVectorReg, zeroReg, tempReg);
      generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, searchVectorReg, targetVectorNotReg, searchVectorReg, permuteVectorReg);
      }
   else
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::lvsl, node, permuteVectorReg, zeroReg, tempReg);
      generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, searchVectorReg, searchVectorReg, targetVectorNotReg, permuteVectorReg);
      }

   /*
    * Now the search vector is ready for comparison: none of the garbage can compare as equal to
    * our target value and the start of the vector is now aligned to the start of the string. So
    * we can now perform the comparison as normal. If it isn't found, jump to notFoundLabel to return -1.
    */
   generateTrg1Src2Instruction(cg, vectorCompareOp, node, vec2Reg, searchVectorReg, targetVectorReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, notFoundLabel, cr6Reg);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, foundEndVectorLabel);

   /*
    * We've managed to find a match for the target value in the loaded vector, but we don't yet know
    * which element of the loaded vector is the first match. The comparison will have set matching
    * elements in the vector to -1 and non-matching elements to 0. We can find the first matching
    * element by gathering the first bit of every byte in the vector register.
    */

   /* Set permuteVector = 0x000102030405060708090a0b0c0d0e0f */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::lvsl, node, permuteVectorReg, zeroReg, zeroReg);

   /*
    * For little-endian, reverse permuteVector so that we can find the first set bit using a count
    * leading zeroes test instead of a count trailing zeroes test. This is necessary since cnttzw
    * wasn't introduced until Power 9.
    */
   if (comp->target().cpu.isLittleEndian())
      {
      generateTrg1ImmInstruction(cg, TR::InstOpCode::vspltisb, node, vec1Reg, 15);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::vsububm, node, permuteVectorReg, vec1Reg, permuteVectorReg);
      }

   /* Set permuteVector = 0x00081018202830384048505860687078 (reversed for LE) */
   generateTrg1ImmInstruction(cg, TR::InstOpCode::vspltisb, node, vec1Reg, 3);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::vslb, node, permuteVectorReg, permuteVectorReg, vec1Reg);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, foundEndVectorRetryLabel);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::vbpermq, node, vec1Reg, vec2Reg, permuteVectorReg);
   generateTrg1Src1Instruction(cg, TR::InstOpCode::mfvsrwz, node, resultReg, vec1Reg);

   /*
    * Then count the number of leading zeroes from the obtained result. This tells us the index (in
    * bytes) of the first matching element in the vector. Note that there is no way to count leading
    * zeroes of a half-word, so we count leading zeroes of a word and subtract 16 since the value
    * we're interested in is in the least significant half-word.
    */
   generateTrg1Src1Instruction(cg, TR::InstOpCode::cntlzw, node, resultReg, resultReg);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, resultReg, resultReg, -16);

   /*
    * Finally, combine this with the address of the last vector load to find the address of the
    * first matching element in the string.
    */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, currentAddressReg, resultReg, currentAddressReg);

   /*
    * The character at currentAddressReg is now known to match the first character of the substring.
    * Now code to perform an arraycmp is inserted here to verify that the next characters match the rest of the substring as well.
    * On a full match, it jumps to foundLabel to calculate the final result to return.
    * If it isn't a full match, the code path continues on here.
    */
   performArrayCmp(node, cg, currentAddressReg, subStringAddressReg, subStringLenReg, tempReg, temp2Reg, subSearchOffsetReg, vec1Reg, vec2Reg, cr0Reg, cr6Reg, foundLabel, nullptr);

   /* A match at currentAddressReg was not found so we undo adding in the index of the match that was found. */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::subf, node, currentAddressReg, resultReg, currentAddressReg);

   /* The first character match that was found is masked out in searchVectorReg so we can check for a second match. */
   generateTrg1Src2Instruction(cg, comp->target().cpu.isLittleEndian() ? TR::InstOpCode::lvsl : TR::InstOpCode::lvsr, node, vec2Reg, zeroReg, resultReg);
   generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, vec1Reg, maskVectorReg, maskVectorReg, vec2Reg);
   generateTrg1Src3Instruction(cg, TR::InstOpCode::vsel, node, searchVectorReg, searchVectorReg, targetVectorNotReg, vec1Reg);

   /*
    * This checks for a second match. If one isn't found, jump to notFoundLabel to return -1.
    * If a second match is found, jump back to foundEndVectorRetryLabel to try the process again.
    * The value in permuteVectorReg has not changed since it was generated so it doesn't need to be generated again.
    */
   generateTrg1Src2Instruction(cg, vectorCompareOp, node, vec2Reg, searchVectorReg, targetVectorReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, notFoundLabel, cr6Reg);
   generateLabelInstruction(cg, TR::InstOpCode::b, node, foundEndVectorRetryLabel);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, notSmallLabel);

   /*
    * Check if we already have 16-byte alignment. Vector loads require 16-byte alignment, so if we
    * aren't properly aligned, we'll need to handle comparisons specially until we achieve 16-byte
    * alignment. At this point, tempReg is equal to currentAddressReg with the lowest 4 bits clear.
    */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp8, node, cr0Reg, currentAddressReg, tempReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, vectorLoopLabel, cr0Reg);

   /*
    * We are not on a 16-byte boundary, so we cannot directly load the first 16 bytes of the string
    * for comparison. Instead, we load the 16 byte vector starting from the 16-byte aligned section
    * containing the start of the string. Since we have 16-byte natural alignment, this can't cause
    * an unexpected page fault.
    */
   generateTrg1MemInstruction(cg, TR::InstOpCode::lvx, node, searchVectorReg, TR::MemoryReference::createWithIndexReg(cg, zeroReg, currentAddressReg, 16));

   /*
    * However, before we can run any comparisons on the loaded vector, we must ensure that the extra
    * garbage read before the start of the string can't match the target character. To do this, we
    * shift the loaded vector backwards by n bytes shifting in the bitwise complement of the target
    * character.
    */
   if (comp->target().cpu.isLittleEndian())
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::lvsr, node, permuteVectorReg, zeroReg, currentAddressReg); // Only the low 4 bits in currentAddressReg are used so it acts as a value between 0-15.
      generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, searchVectorReg, targetVectorNotReg, searchVectorReg, permuteVectorReg);
      }
   else
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::lvsl, node, permuteVectorReg, zeroReg, currentAddressReg); // Only the low 4 bits in currentAddressReg are used so it acts as a value between 0-15.
      generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, searchVectorReg, searchVectorReg, targetVectorNotReg, permuteVectorReg);
      }

   /*
    * Now our vector is ready for comparison: no garbage can match the target value and the start of
    * the vector is now aligned to the start of the string.
    * The value loaded into searchVectorReg is compared with targetVectorReg to see if any of the characters matches the first character of the substring.
    * If so, this jumps to foundFirstVectorLabel to check the rest of the substring. If it doesn't match, it falls through to firstContinueLabel.
    * If this branch is taken but the rest of the substring does not match, it will jump back to firstContinueLabel.
    */
   generateTrg1Src2Instruction(cg, vectorCompareOp, node, vec2Reg, searchVectorReg, targetVectorReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, foundFirstVectorLabel, cr6Reg);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, firstContinueLabel);

   generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldicr, node, currentAddressReg, currentAddressReg, 0, CONSTANT64(0xfffffffffffffff0));
   /* If the first vector didn't match, then we can slide right into the standard vectorized loop. */
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, currentAddressReg, currentAddressReg, 16);

   /* This checks to see if there are any characters left to look at. */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp8, node, cr0Reg, currentAddressReg, endAddressReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bge, node, notFoundLabel, cr0Reg);

   /* This checks to see if there are less than 16 bytes of data left to look at. */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp8, node, cr0Reg, currentAddressReg, temp2Reg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, residueLabel, cr0Reg);

   /* This is the heart of the vectorized loop, working just like any standard vectorized loop. */
   generateLabelInstruction(cg, TR::InstOpCode::label, node, vectorLoopLabel);
   generateTrg1MemInstruction(cg, TR::InstOpCode::lvx, node, searchVectorReg, TR::MemoryReference::createWithIndexReg(cg, zeroReg, currentAddressReg, 16));

   /*
    * The value loaded into searchVectorReg is again compared with targetVectorReg to see if any of the characters matches the first character of the substring.
    * If so, this jumps to foundVectorLabel to check the rest of the substring. If it doesn't match, it falls through to vectorContinueLabel.
    * If this branch is taken but the rest of the substring does not match, it will jump back to vectorContinueLabel.
    */
   generateTrg1Src2Instruction(cg, vectorCompareOp, node, vec2Reg, searchVectorReg, targetVectorReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, foundVectorLabel, cr6Reg);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, vectorContinueLabel);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, currentAddressReg, currentAddressReg, 16);

   /* This checks to see if there are less than 16 bytes of data left to look at. */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp8, node, cr0Reg, currentAddressReg, temp2Reg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, vectorLoopLabel, cr0Reg);

   /* This checks to see if there are any characters left to look at. */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp8, node, cr0Reg, currentAddressReg, endAddressReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bge, node, notFoundLabel, cr0Reg);

   /* Now we're done with the part of the loop which can be handled as a normal vectorized loop. From here we handle the residue. */
   generateLabelInstruction(cg, TR::InstOpCode::label, node, residueLabel);

   /*
    * Usually, we would need a residue loop here, but it's safe to read beyond the end of the string
    * here. Since our load will have 16-byte natural alignment, it can't cross a page boundary and
    * cause an unexpected page fault.
    */
   generateTrg1MemInstruction(cg, TR::InstOpCode::lvx, node, searchVectorReg, TR::MemoryReference::createWithIndexReg(cg, zeroReg, currentAddressReg, 16));

   /*
    * Before we can run our comparison, we need to ensure that the garbage from beyond the end of
    * the string cannot compare as equal to our target value. To do this, we first rotate the vector
    * forwards by n bytes then shift back by n bytes, shifting in the bitwise complement of the
    * target value.
    */
   generateTrg1Src1Instruction(cg, TR::InstOpCode::neg, node, tempReg, endAddressReg); // Only the low 4 bits in endAddressReg are used so it acts as a value between 0-15.
   if (comp->target().cpu.isLittleEndian())
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::lvsl, node, permuteVectorReg, zeroReg, tempReg);
      generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, searchVectorReg, searchVectorReg, searchVectorReg, permuteVectorReg);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::lvsr, node, permuteVectorReg, zeroReg, tempReg);
      generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, searchVectorReg, targetVectorNotReg, searchVectorReg, permuteVectorReg);
      }
   else
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::lvsr, node, permuteVectorReg, zeroReg, tempReg);
      generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, searchVectorReg, searchVectorReg, searchVectorReg, permuteVectorReg);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::lvsl, node, permuteVectorReg, zeroReg, tempReg);
      generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, searchVectorReg, searchVectorReg, targetVectorNotReg, permuteVectorReg);
      }

   /* Now we run our comparison as normal. */
   generateTrg1Src2Instruction(cg, vectorCompareOp, node, vec2Reg, searchVectorReg, targetVectorReg);

   /*
    * If a first character match is found, jump to foundEndVectorLabel to check the rest of the substring.
    * If there isn't a match, it falls through to notFoundLabel to return -1.
    * If this branch is taken but the rest of the substring does not match, it will jump back to notFoundLabel to return -1.
    */
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, foundEndVectorLabel, cr6Reg);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, notFoundLabel);

   /* We've looked through the entire string and didn't find the substring, so return the sentinel value -1. */
   generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, resultReg, -1);
   generateLabelInstruction(cg, TR::InstOpCode::b, node, endLabel);

   /*
    * This section handles the case where the first character of the substring matches the first character of the main string (adjusted for offset).
    * performArrayCmp generates arraycmp code to handle checking the rest of the substring. On a match, jump to foundExactLabel since returnReg already has the offset to return.
    * If it doesn't match, jump to notFoundEarlyLabel to continue searching the rest of the main string.
    * The end of the assembly generated by performArrayCmp is a fall through so a branch to notFoundEarlyLabel is also needed here.
    */
   generateLabelInstruction(cg, TR::InstOpCode::label, node, foundEarlyLabel);
   performArrayCmp(node, cg, currentAddressReg, subStringAddressReg, subStringLenReg, tempReg, temp2Reg, subSearchOffsetReg, vec1Reg, vec2Reg, cr0Reg, cr6Reg, foundExactLabel, notFoundEarlyLabel);
   generateLabelInstruction(cg, TR::InstOpCode::b, node, notFoundEarlyLabel);

   /*
    * This section handles the case where the characters to search for in the main string do not fit in a single vector load (not small case).
    * More specifically, this is during the search before the vector loop.
    */
   generateLabelInstruction(cg, TR::InstOpCode::label, node, foundFirstVectorLabel);

   /* Set permuteVector = 0x000102030405060708090a0b0c0d0e0f */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::lvsl, node, permuteVectorReg, zeroReg, zeroReg);

   /*
    * For little-endian, reverse permuteVector so that we can find the first set bit using a count
    * leading zeroes test instead of a count trailing zeroes test. This is necessary since cnttzw
    * wasn't introduced until Power 9.
    */
   if (comp->target().cpu.isLittleEndian())
      {
      generateTrg1ImmInstruction(cg, TR::InstOpCode::vspltisb, node, vec1Reg, 15);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::vsububm, node, permuteVectorReg, vec1Reg, permuteVectorReg);
      }

   /* Set permuteVector = 0x00081018202830384048505860687078 (reversed for LE) */
   generateTrg1ImmInstruction(cg, TR::InstOpCode::vspltisb, node, vec1Reg, 3);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::vslb, node, permuteVectorReg, permuteVectorReg, vec1Reg);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, foundFirstVectorRetryLabel);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::vbpermq, node, vec1Reg, vec2Reg, permuteVectorReg);
   generateTrg1Src1Instruction(cg, TR::InstOpCode::mfvsrwz, node, resultReg, vec1Reg);

   /*
    * Then count the number of leading zeroes from the obtained result. This tells us the index (in
    * bytes) of the first matching element in the vector. Note that there is no way to count leading
    * zeroes of a half-word, so we count leading zeroes of a word and subtract 16 since the value
    * we're interested in is in the least significant half-word.
    */
   generateTrg1Src1Instruction(cg, TR::InstOpCode::cntlzw, node, resultReg, resultReg);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, resultReg, resultReg, -16);

   /*
    * Finally, combine this with the address of the last vector load to find the address of the
    * first matching element in the string.
    */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, currentAddressReg, resultReg, currentAddressReg);

   /*
    * The character at currentAddressReg is now known to match the first character of the substring.
    * Now code to perform an arraycmp is inserted here to verify that the next characters match the rest of the substring as well.
    * On a full match, it jumps to foundLabel to calculate the final result to return.
    * If it isn't a full match, the code path continues on here.
    */
   performArrayCmp(node, cg, currentAddressReg, subStringAddressReg, subStringLenReg, tempReg, temp2Reg, subSearchOffsetReg, vec1Reg, vec2Reg, cr0Reg, cr6Reg, foundLabel, nullptr);

   /* A match at currentAddressReg was not found so we undo adding in the index of the match that was found. */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::subf, node, currentAddressReg, resultReg, currentAddressReg);

   /* The first character match that was found is masked out in searchVectorReg so we can check for a second match. */
   generateTrg1Src2Instruction(cg, comp->target().cpu.isLittleEndian() ? TR::InstOpCode::lvsl : TR::InstOpCode::lvsr, node, vec2Reg, zeroReg, resultReg);
   generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, vec1Reg, maskVectorReg, maskVectorReg, vec2Reg);
   generateTrg1Src3Instruction(cg, TR::InstOpCode::vsel, node, searchVectorReg, searchVectorReg, targetVectorNotReg, vec1Reg);

   /*
    * This checks for a second match. If one isn't found, jump to firstContinueLabel to keep searching.
    * If a second match is found, jump back to foundFirstVectorRetryLabel to try the process again.
    * The value in permuteVectorReg has not changed since it was generated so it doesn't need to be generated again.
    */
   generateTrg1Src2Instruction(cg, vectorCompareOp, node, vec2Reg, searchVectorReg, targetVectorReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, foundFirstVectorRetryLabel, cr6Reg);
   generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldicr, node, temp2Reg, endAddressReg, 0, CONSTANT64(0xfffffffffffffff0));
   generateLabelInstruction(cg, TR::InstOpCode::b, node, firstContinueLabel);

   /*
    * This section handles the case where the characters to search for in the main string do not fit in a single vector load (not small case).
    * More specifically, this is during the vector loop.
    */
   generateLabelInstruction(cg, TR::InstOpCode::label, node, foundVectorLabel);

   /* Set permuteVector = 0x000102030405060708090a0b0c0d0e0f */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::lvsl, node, permuteVectorReg, zeroReg, zeroReg);

   /*
    * For little-endian, reverse permuteVector so that we can find the first set bit using a count
    * leading zeroes test instead of a count trailing zeroes test. This is necessary since cnttzw
    * wasn't introduced until Power 9.
    */
   if (comp->target().cpu.isLittleEndian())
      {
      generateTrg1ImmInstruction(cg, TR::InstOpCode::vspltisb, node, vec1Reg, 15);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::vsububm, node, permuteVectorReg, vec1Reg, permuteVectorReg);
      }

   /* Set permuteVector = 0x00081018202830384048505860687078 (reversed for LE) */
   generateTrg1ImmInstruction(cg, TR::InstOpCode::vspltisb, node, vec1Reg, 3);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::vslb, node, permuteVectorReg, permuteVectorReg, vec1Reg);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, foundVectorRetryLabel);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::vbpermq, node, vec1Reg, vec2Reg, permuteVectorReg);
   generateTrg1Src1Instruction(cg, TR::InstOpCode::mfvsrwz, node, resultReg, vec1Reg);

   /*
    * Then count the number of leading zeroes from the obtained result. This tells us the index (in
    * bytes) of the first matching element in the vector. Note that there is no way to count leading
    * zeroes of a half-word, so we count leading zeroes of a word and subtract 16 since the value
    * we're interested in is in the least significant half-word.
    */
   generateTrg1Src1Instruction(cg, TR::InstOpCode::cntlzw, node, resultReg, resultReg);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, resultReg, resultReg, -16);

   /*
    * Finally, combine this with the address of the last vector load to find the address of the
    * first matching element in the string.
    */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, currentAddressReg, resultReg, currentAddressReg);

   /*
    * The character at currentAddressReg is now known to match the first character of the substring.
    * Now code to perform an arraycmp is inserted here to verify that the next characters match the rest of the substring as well.
    * On a full match, it jumps to foundLabel to calculate the final result to return.
    * If it isn't a full match, the code path continues on here.
    */
   performArrayCmp(node, cg, currentAddressReg, subStringAddressReg, subStringLenReg, tempReg, temp2Reg, subSearchOffsetReg, vec1Reg, vec2Reg, cr0Reg, cr6Reg, foundLabel, nullptr);

   /* A match at currentAddressReg was not found so we undo adding in the index of the match that was found. */
   generateTrg1Src2Instruction(cg, TR::InstOpCode::subf, node, currentAddressReg, resultReg, currentAddressReg);

   /* The first character match that was found is masked out in searchVectorReg so we can check for a second match. */
   generateTrg1Src2Instruction(cg, comp->target().cpu.isLittleEndian() ? TR::InstOpCode::lvsl : TR::InstOpCode::lvsr, node, vec2Reg, zeroReg, resultReg);
   generateTrg1Src3Instruction(cg, TR::InstOpCode::vperm, node, vec1Reg, maskVectorReg, maskVectorReg, vec2Reg);
   generateTrg1Src3Instruction(cg, TR::InstOpCode::vsel, node, searchVectorReg, searchVectorReg, targetVectorNotReg, vec1Reg);

   /*
    * This checks for a second match. If one isn't found, jump to vectorContinueLabel to keep searching.
    * If a second match is found, jump back to foundVectorRetryLabel to try the process again.
    * The value in permuteVectorReg has not changed since it was generated so it doesn't need to be generated again.
    */
   generateTrg1Src2Instruction(cg, vectorCompareOp, node, vec2Reg, searchVectorReg, targetVectorReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, foundVectorRetryLabel, cr6Reg);
   generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldicr, node, temp2Reg, endAddressReg, 0, CONSTANT64(0xfffffffffffffff0));
   generateLabelInstruction(cg, TR::InstOpCode::b, node, vectorContinueLabel);

   /* A full match of the substring inside the main string has been found. The index is calculated and returned in resultReg. */
   generateLabelInstruction(cg, TR::InstOpCode::label, node, foundLabel);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::subf, node, resultReg, arrayAddressReg, currentAddressReg);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, foundExactLabel);

   /* Under UTF16, resultReg is doubled to properly calculate index for 2 byte long characters. */
   if (!isLatin1)
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::srawi, node, resultReg, resultReg, 1);

   TR::RegisterDependencyConditions *deps = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, numDependencies, cg->trMemory());

   if (!cg->canClobberNodesRegister(node->getChild(0)))
      {
      deps->addPostCondition(arrayAddressReg, TR::RealRegister::NoReg);
      }

   if (!cg->canClobberNodesRegister(node->getChild(1)))
      {
      deps->addPostCondition(endAddressReg, TR::RealRegister::NoReg);
      deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();
      }

   if (!cg->canClobberNodesRegister(node->getChild(2)))
      {
      deps->addPostCondition(subStringAddressReg, TR::RealRegister::NoReg);
      deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();
      }

   if (!cg->canClobberNodesRegister(node->getChild(3)))
      {
      deps->addPostCondition(subStringLenReg, TR::RealRegister::NoReg);
      }

   if (!cg->canClobberNodesRegister(node->getChild(4)))
      {
      deps->addPostCondition(resultReg, TR::RealRegister::NoReg);
      deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();
      }

   deps->addPostCondition(arrayObjReg, TR::RealRegister::NoReg);
   deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();

   deps->addPostCondition(arrayLenReg, TR::RealRegister::NoReg);
   deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();

   deps->addPostCondition(subStringObjReg, TR::RealRegister::NoReg);
   deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();

   deps->addPostCondition(strCountReg, TR::RealRegister::NoReg);

   deps->addPostCondition(fromIndexReg, TR::RealRegister::NoReg);
   deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();

   deps->addPostCondition(currentAddressReg, TR::RealRegister::NoReg);
   deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();
   deps->addPostCondition(zeroReg, TR::RealRegister::NoReg);
   deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();
   deps->addPostCondition(tempReg, TR::RealRegister::NoReg);
   deps->addPostCondition(temp2Reg, TR::RealRegister::NoReg);
   deps->addPostCondition(subSearchOffsetReg, TR::RealRegister::NoReg);
   deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();

   deps->addPostCondition(vec1Reg, TR::RealRegister::NoReg);
   deps->addPostCondition(vec2Reg, TR::RealRegister::NoReg);
   deps->addPostCondition(targetVectorReg, TR::RealRegister::NoReg);
   deps->addPostCondition(targetVectorNotReg, TR::RealRegister::NoReg);
   deps->addPostCondition(searchVectorReg, TR::RealRegister::NoReg);
   deps->addPostCondition(permuteVectorReg, TR::RealRegister::NoReg);
   deps->addPostCondition(maskVectorReg, TR::RealRegister::NoReg);
   deps->addPostCondition(cr0Reg, TR::RealRegister::cr0);
   deps->addPostCondition(cr6Reg, TR::RealRegister::cr6);

   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, endLabel, deps);

   deps->stopUsingDepRegs(cg, resultReg);

   node->setRegister(resultReg);

   for (int32_t i = 0; i < node->getNumChildren(); i++)
      {
      cg->decReferenceCount(node->getChild(i));
      }

   return resultReg;
   }

static bool inlineIntrinsicInflate(TR::Node *node, TR::CodeGenerator *cg)
   {
   static bool disableStringInflateIntrinsic = feGetEnv("TR_DisableStringInflateIntrinsic") != NULL;
   if (disableStringInflateIntrinsic)
      {
      return false;
      }

   TR_ASSERT_FATAL(cg->getSupportsInlineStringLatin1Inflate(), "This evaluator should only be triggered when inlining StringLatin1.inflate([BI[CII)V is enabled on Java 11 onwards!\n");

   TR::Compilation *comp = cg->comp();
   bool isAtLeastP9 = comp->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P9);
   bool isLE = comp->target().cpu.isLittleEndian();

   TR::Register *inputAddressReg = cg->gprClobberEvaluate(node->getChild(0));
   TR::Register *inputOffsetReg = cg->gprClobberEvaluate(node->getChild(1));
   TR::Register *outputAddressReg = cg->gprClobberEvaluate(node->getChild(2));
   TR::Register *outputOffsetReg = cg->gprClobberEvaluate(node->getChild(3));
   TR::Register *remainingReg = cg->gprClobberEvaluate(node->getChild(4));

   TR::Register *tempReg = inputOffsetReg;
   TR::Register *maskReg = outputOffsetReg;

   TR::Register *vec1Reg = cg->allocateRegister(TR_VRF);
   TR::Register *vec2Reg = cg->allocateRegister(TR_VRF);
   TR::Register *constZeroVecReg = cg->allocateRegister(TR_VRF);
   TR::Register *constNeg16VecReg = (isLE && !isAtLeastP9) ? cg->allocateRegister(TR_VRF) : nullptr;

   TR::Register *condReg = cg->allocateRegister(TR_CCR);

   TR::LabelSymbol *startLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *load16LoopLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *load8Label = generateLabelSymbol(cg);
   TR::LabelSymbol *load4Label = generateLabelSymbol(cg);
   TR::LabelSymbol *load1Label = generateLabelSymbol(cg);
   TR::LabelSymbol *doneLabel = generateLabelSymbol(cg);

   int32_t numRegs = constNeg16VecReg ? 10 : 9;
   TR::RegisterDependencyConditions *dependencies = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, numRegs, cg->trMemory());
   dependencies->addPostCondition(inputAddressReg, TR::RealRegister::NoReg);
   dependencies->getPostConditions()->getRegisterDependency(0)->setExcludeGPR0();
   dependencies->addPostCondition(inputOffsetReg, TR::RealRegister::NoReg);
   dependencies->addPostCondition(outputAddressReg, TR::RealRegister::NoReg);
   dependencies->getPostConditions()->getRegisterDependency(2)->setExcludeGPR0();
   dependencies->addPostCondition(outputOffsetReg, TR::RealRegister::NoReg);
   dependencies->addPostCondition(remainingReg, TR::RealRegister::NoReg);
   dependencies->getPostConditions()->getRegisterDependency(4)->setExcludeGPR0();
   dependencies->addPostCondition(vec1Reg, TR::RealRegister::NoReg);
   dependencies->addPostCondition(vec2Reg, TR::RealRegister::NoReg);
   dependencies->addPostCondition(constZeroVecReg, TR::RealRegister::NoReg);
   if (constNeg16VecReg)
      {
      dependencies->addPostCondition(constNeg16VecReg, TR::RealRegister::NoReg);
      }
   dependencies->addPostCondition(condReg, TR::RealRegister::cr0);

   cg->generateDebugCounter(TR::DebugCounter::debugCounterName(comp, "inlineIntrinsicInflate/(%s)", comp->signature()));

   generateLabelInstruction(cg, TR::InstOpCode::label, node, startLabel, NULL);
   startLabel->setStartInternalControlFlow();

   /* If remainingReg equals 0, there is no work to be done. Immediate jump to the end. */
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, condReg, remainingReg, 0);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, condReg);

   // IMPORTANT: The upper 32 bits of a 64-bit register containing an int are undefined. Since the
   // indices are being passed in as ints, we must ensure that their upper 32 bits are not garbage.
   generateTrg1Src1Instruction(cg, TR::InstOpCode::extsw, node, inputOffsetReg, inputOffsetReg);
   generateTrg1Src1Instruction(cg, TR::InstOpCode::extsw, node, outputOffsetReg, outputOffsetReg);

   /*
    * Determine the address of the first byte to read either by loading from dataAddr or adding the header size.
    * This is followed by adding in the offset.
    */
#if defined(J9VM_GC_SPARSE_HEAP_ALLOCATION)
   if (TR::Compiler->om.isOffHeapAllocationEnabled())
      {
      generateTrg1MemInstruction(cg, TR::InstOpCode::ld, node, inputAddressReg, TR::MemoryReference::createWithDisplacement(cg, inputAddressReg, TR::Compiler->om.offsetOfContiguousDataAddrField(), 8));
      }
   else
#endif /* J9VM_GC_SPARSE_HEAP_ALLOCATION */
      {
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, inputAddressReg, inputAddressReg, TR::Compiler->om.contiguousArrayHeaderSizeInBytes());
      }

   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, inputAddressReg, inputAddressReg, inputOffsetReg);

   /*
    * Determine the address of the first char to store either by loading from dataAddr or adding the header size.
    * This is followed by adding in the offset twice due to being char data.
    */
#if defined(J9VM_GC_SPARSE_HEAP_ALLOCATION)
   if (TR::Compiler->om.isOffHeapAllocationEnabled())
      {
      generateTrg1MemInstruction(cg, TR::InstOpCode::ld, node, outputAddressReg, TR::MemoryReference::createWithDisplacement(cg, outputAddressReg, TR::Compiler->om.offsetOfContiguousDataAddrField(), 8));
      }
   else
#endif /* J9VM_GC_SPARSE_HEAP_ALLOCATION */
      {
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, outputAddressReg, outputAddressReg, TR::Compiler->om.contiguousArrayHeaderSizeInBytes());
      }

   generateShiftLeftImmediate(cg, node, outputOffsetReg, outputOffsetReg, 1);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, outputAddressReg, outputAddressReg, outputOffsetReg);

   /* If 1-3 bytes remain, jump to load1Label to handle them by by byte. */
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, condReg, remainingReg, 4);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, load1Label, condReg);

   /* If 4-7 bytes remain, first jump to load4Label to handle the first 4 bytes. */
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, condReg, remainingReg, 8);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, load4Label, condReg);

   /* Initialize constZeroVecReg to all 0s. This is needed in the 8 byte and 16 byte load cases. */
   generateTrg1ImmInstruction(cg, TR::InstOpCode::vspltisw, node, constZeroVecReg, 0);
   if (isLE && !isAtLeastP9)
      {
      /*
       * Initialize constNeg16VecReg to [-16, -16, -16, -16].
       * This vector is used to fix the order of bytes during processing.
       * It is only used under Little endian on Power 8.
       * This is needed in the 8 byte and 16 byte load cases.
       */
      generateTrg1ImmInstruction(cg, TR::InstOpCode::vspltisw, node, constNeg16VecReg, -16);
      }

   /* If 8-15 bytes remain, first jump to load8Label to handle the first 8 bytes. */
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, condReg, remainingReg, 16);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, load8Label, condReg);

   /* Initialize ctr with the number of 16 byte loads that can be performed. */
   generateShiftRightLogicalImmediate(cg, node, tempReg, remainingReg, 4);
   generateSrc1Instruction(cg, TR::InstOpCode::mtctr, node, tempReg);

   /* Initialize tempReg to 16. This is used to store at an offset of 16. */
   generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, tempReg, 16);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, load16LoopLabel, NULL);

   /*
    * 16 byte load section
    * Power9 BE case:
    *   lxvb16x  vec1Reg, r0, inputAddressReg        //eg. vec1Reg = 01020304_05060708_090a0b0c_0d0e0f10
    *   vmrghb   vec2Reg, constZeroVecReg, vec1Reg   //Interlaces 0x00 to convert the bytes to char, vec2Reg = 0x00010002_00030004_00050006_00070008
    *   stxvb16x vec2Reg, r0, outputAddressReg       //Stores vec2Reg to memory. 00 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08
    *   vmrglb   vec1Reg, constZeroVecReg, vec1Reg   //Interlaces 0x00 to convert the bytes to char, vec1Reg = 0x0009000a_000b000c_000d000e_000f0010
    *   stxvb16x vec1Reg, outputAddressReg, tempReg  //Stores vec1Reg to memory. 00 09 00 0a 00 0b 00 0c 00 0d 00 0e 00 0f 00 10
    * Power9 LE case:
    *   lxvb16x  vec1Reg, r0, inputAddressReg        //eg. vec1Reg = 01020304_05060708_090a0b0c_0d0e0f10
    *   vmrghb   vec2Reg, vec1Reg, constZeroVecReg   //Interlaces 0x00 to convert the bytes to char, vec2Reg = 0x01000200_03000400_05000600_07000800
    *   stxvb16x vec2Reg, r0, outputAddressReg       //Stores vec2Reg to memory. 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08 00
    *   vmrglb   vec1Reg, vec1Reg, constZeroVecReg   //Interlaces 0x00 to convert the bytes to char, vec2Reg = 0x09000a00_0b000c00_0d000e00_0f001000
    *   stxvb16x vec1Reg, outputAddressReg, tempReg  //Stores vec1Reg to memory. 09 00 0a 00 0b 00 0c 00 0d 00 0e 00 0f 00 10 00
    * Power8 BE case:
    *   lxvw4x   vec1Reg, r0, inputAddressReg        //eg. vec1Reg = 01020304_05060708_090a0b0c_0d0e0f10
    *   vmrghb   vec2Reg, constZeroVecReg, vec1Reg   //Interlaces 0x00 to convert the bytes to char, vec2Reg = 0x00010002_00030004_00050006_00070008
    *   stxvw4x  vec2Reg, r0, outputAddressReg       //Stores vec2Reg to memory. 00 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08
    *   vmrglb   vec1Reg, constZeroVecReg, vec1Reg   //Interlaces 0x00 to convert the bytes to char, vec1Reg = 0x0009000a_000b000c_000d000e_000f0010
    *   stxvw4x  vec1Reg, outputAddressReg, tempReg  //Stores vec1Reg to memory. 00 09 00 0a 00 0b 00 0c 00 0d 00 0e 00 0f 00 10
    * Power8 LE case:
    *   lxvw4x   vec1Reg, r0, inputAddressReg        //eg. vec1Reg = 0x04030201_08070605_0c0b0a09_100f0e0d, LE load mixes up the byte order.
    *   vrlw     vec1Reg, vec1Reg, constNeg16VecReg  //Rotate by -16, vec1Reg = 0x02010403_06050807_0a090c0b_0e0d100f
    *   vmrghb   vec2Reg, constZeroVecReg, vec1Reg   //Interlaces 0x00 to convert the bytes to char, vec2Reg = 0x00020001_00040003_00060005_00080007
    *   stxvw4x  vec2Reg, r0, outputAddressReg       //This puts the chars in the right order in memory. 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08 00
    *   vmrglb   vec1Reg, constZeroVecReg, vec1Reg   //Interlaces 0x00 to convert the bytes to char, vec1Reg = 0x000a0009_000c000b_000e000d_0010000f
    *   stxvw4x  vec1Reg, outputAddressReg, tempReg  //This puts the chars in the right order in memory. 09 00 0a 00 0b 00 0c 00 0d 00 0e 00 0f 00 10 00
    */
   if (isAtLeastP9)
      {
      generateTrg1MemInstruction(cg, TR::InstOpCode::lxvb16x, node, vec1Reg, TR::MemoryReference::createWithIndexReg(cg, NULL, inputAddressReg, 16));
      }
   else
      {
      generateTrg1MemInstruction(cg, TR::InstOpCode::lxvw4x, node, vec1Reg, TR::MemoryReference::createWithIndexReg(cg, NULL, inputAddressReg, 16));

      if (isLE)
         {
         generateTrg1Src2Instruction(cg, TR::InstOpCode::vrlw, node, vec1Reg, vec1Reg, constNeg16VecReg);
         }
      }

   if (isLE && isAtLeastP9)
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::vmrghb, node, vec2Reg, vec1Reg, constZeroVecReg);
      }
   else
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::vmrghb, node, vec2Reg, constZeroVecReg, vec1Reg);
      }

   if (isAtLeastP9)
      {
      generateMemSrc1Instruction(cg, TR::InstOpCode::stxvb16x, node, TR::MemoryReference::createWithIndexReg(cg, NULL, outputAddressReg, 16), vec2Reg);
      }
   else
      {
      generateMemSrc1Instruction(cg, TR::InstOpCode::stxvw4x, node, TR::MemoryReference::createWithIndexReg(cg, NULL, outputAddressReg, 16), vec2Reg);
      }

   if (isLE && isAtLeastP9)
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::vmrglb, node, vec1Reg, vec1Reg, constZeroVecReg);
      }
   else
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::vmrglb, node, vec1Reg, constZeroVecReg, vec1Reg);
      }

   if (isAtLeastP9)
      {
      generateMemSrc1Instruction(cg, TR::InstOpCode::stxvb16x, node, TR::MemoryReference::createWithIndexReg(cg, outputAddressReg, tempReg, 16), vec1Reg);
      }
   else
      {
      generateMemSrc1Instruction(cg, TR::InstOpCode::stxvw4x, node, TR::MemoryReference::createWithIndexReg(cg, outputAddressReg, tempReg, 16), vec1Reg);
      }

   /* 16 bytes were loaded and 32 bytes were stored so bump up inputAddressReg and outputAddressReg by those amounts. */
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, inputAddressReg, inputAddressReg, 16);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, outputAddressReg, outputAddressReg, 32);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bdnz, node, load16LoopLabel, condReg);

   /* Update remainingReg to account for all the possible 16 byte loads being done by the vector loop */
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, remainingReg, remainingReg, condReg, 0xF);

   /* If remainingReg is 0, jump to the end. */
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, condReg);

   /* If 1-3 bytes remain, jump to load1Label to handle them by by byte. */
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, condReg, remainingReg, 4);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, load1Label, condReg);

   /* If 4-7 bytes remain, first jump to load4Label to handle the first 4 bytes. */
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, condReg, remainingReg, 8);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, load4Label, condReg);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, load8Label, NULL);

   /* 8 byte load section
    * Power9 BE case:
    *   ldx      tempReg, NULL, inputAddressReg      // eg. tempReg = 0x01020304_05060708
    *   mtvsrd   vec1Reg, tempReg                    // Move data into vector reg. vec1Reg = 0x01020304_05060708_XXXXXXXX_XXXXXXXX
    *   vmrghb   vec1Reg, constZeroVecReg, vec1Reg   // Interlaces 0x00 to convert the bytes to char, vec1Reg = 0x00010002_00030004_00050006_00070008
    *   stxvb16x vec1Reg, NULL, outputAddressReg     // Stores vec1Reg to memory. 00 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08
    * Power9 LE case:
    *   ldbrx    tempReg, NULL, inputAddressReg      // eg. tempReg = 0x01020304_05060708
    *   mtvsrd   vec1Reg, tempReg                    // Move data into vector reg. vec1Reg = 0x01020304_05060708_XXXXXXXX_XXXXXXXX
    *   vmrghb   vec1Reg, vec1Reg, constZeroVecReg   // Interlaces 0x00 to convert the bytes to char, vec1Reg = 0x01000200_03000400_05000600_07000800
    *   stxvb16x vec1Reg, NULL, outputAddressReg     // Stores vec1Reg to memory. 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08 00
    * Power8 BE case:
    *   ldx      tempReg, NULL, inputAddressReg      // eg. tempReg = 0x01020304_05060708
    *   mtvsrd   vec1Reg, tempReg                    // Move data into vector reg. vec1Reg = 0x01020304_05060708_XXXXXXXX_XXXXXXXX
    *   vmrghb   vec1Reg, constZeroVecReg, vec1Reg   // Interlaces 0x00 to convert the bytes to char, vec1Reg = 0x00010002_00030004_00050006_00070008
    *   stxvw4x  vec1Reg, NULL, outputAddressReg     // Stores vec1Reg to memory. 00 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08
    * Power8 LE case:
    *   ldbrx    tempReg, NULL, inputAddressReg      // eg. tempReg = 0x01020304_05060708
    *   mtvsrd   vec1Reg, tempReg                    // Move data into vector reg. vec1Reg = 0x01020304_05060708_XXXXXXXX_XXXXXXXX
    *   vmrghb   vec1Reg, constZeroVecReg, vec1Reg   // Interlaces 0x00 to convert the bytes to char, vec1Reg = 0x00010002_00030004_00050006_00070008
    *   vrlw     vec1Reg, vec1Reg, constNeg16VecReg  // Rotate by -16 to adjust byte order for stxvw4x. vec1Reg = 0x00020001_00040003_00060005_00080007
    *   stxvw4x  vec1Reg, NULL, outputAddressReg     // Stores vec1Reg to memory. 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08 00
    */
   if (isLE)
      {
      generateTrg1MemInstruction(cg, TR::InstOpCode::ldbrx, node, tempReg, TR::MemoryReference::createWithIndexReg(cg, NULL, inputAddressReg, 8));
      }
   else
      {
      generateTrg1MemInstruction(cg, TR::InstOpCode::ldx, node, tempReg, TR::MemoryReference::createWithIndexReg(cg, NULL, inputAddressReg, 8));
      }
   generateTrg1Src1Instruction(cg, TR::InstOpCode::mtvsrd, node, vec1Reg, tempReg);
   if (isLE && isAtLeastP9)
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::vmrghb, node, vec1Reg, vec1Reg, constZeroVecReg);
      }
   else
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::vmrghb, node, vec1Reg, constZeroVecReg, vec1Reg);
      }

   if (isLE && !isAtLeastP9)
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::vrlw, node, vec1Reg, vec1Reg, constNeg16VecReg);
      }

   if (isAtLeastP9)
      {
      generateMemSrc1Instruction(cg, TR::InstOpCode::stxvb16x, node, TR::MemoryReference::createWithIndexReg(cg, NULL, outputAddressReg, 16), vec1Reg);
      }
   else
      {
      generateMemSrc1Instruction(cg, TR::InstOpCode::stxvw4x, node, TR::MemoryReference::createWithIndexReg(cg, NULL, outputAddressReg, 16), vec1Reg);
      }

   /* Update remainingReg, jump to the end if 0. */
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, remainingReg, remainingReg, -8);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, condReg, remainingReg, 0);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, condReg);

   /* 8 bytes were loaded and 16 bytes were stored so bump up inputAddressReg and outputAddressReg by those amounts. */
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, inputAddressReg, inputAddressReg, 8);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, outputAddressReg, outputAddressReg, 16);

   /* If 1-3 bytes remain, jump to load1Label to handle them by by byte. */
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, condReg, remainingReg, 4);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, load1Label, condReg);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, load4Label, NULL);

   /*
    * 4 byte load section
    * BE case:
    *   lwzx     tempReg, NULL, inputAddressReg              //eg. tempReg = 0xXXXXXXXX01020304
    *   //initialze maskReg here
    *   rldimi   tempReg, tempReg, 24, 0x00ffffffff000000    //    tempReg = 0xXX01XXXXXX020304
    *   rldimi   tempReg, tempReg, 16, 0x000000ffffff0000    //    tempReg = 0xXX01XX02XXXX0304
    *   rldimi   tempReg, tempReg,  8, 0x0000000000ffff00    //    tempReg = 0xXX01XX02XX03XX04
    *   and      tempReg, tempReg, maskReg                   //    tempReg = 0x0001000200030004
    *   stdx     tempReg, NULL, outputAddressReg             // Stores tempReg to memory: 00 01 00 02 00 03 00 04
    * LE case:
    *   lwzx     tempReg, NULL, inputAddressReg              //eg. tempReg = 0xXXXXXXXX04030201
    *   //initialze maskReg here
    *   rldimi   tempReg, tempReg, 24, 0x00ffffffff000000    //    tempReg = 0xXX04XXXXXX030201
    *   rldimi   tempReg, tempReg, 16, 0x000000ffffff0000    //    tempReg = 0xXX04XX03XXXX0201
    *   rldimi   tempReg, tempReg,  8, 0x0000000000ffff00    //    tempReg = 0xXX04XX03XX02XX01
    *   and      tempReg, tempReg, maskReg                   //    tempReg = 0x0004000300020001
    *   stdx     tempReg, NULL, outputAddressReg             // Stores tempReg to memory: 01 00 02 00 03 00 04 00
    */
   generateTrg1MemInstruction(cg, TR::InstOpCode::lwzx, node, tempReg, TR::MemoryReference::createWithIndexReg(cg, NULL, inputAddressReg, 4));

   /* Initialize maskReg with 0x00ff00ff00ff00ff */
   generateTrg1ImmInstruction(cg, TR::InstOpCode::lis, node, maskReg, 0xFF);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::ori, node, maskReg, maskReg, 0xFF);
   generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldimi, node, maskReg, maskReg, 32, CONSTANT64(0xFFFFFFFF00000000));

   generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldimi, node, tempReg, tempReg, 24, CONSTANT64(0x00FFFFFFFF000000));
   generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldimi, node, tempReg, tempReg, 16, CONSTANT64(0x000000FFFFFF0000));
   generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldimi, node, tempReg, tempReg,  8, CONSTANT64(0x0000000000FFFF00));
   generateTrg1Src2Instruction(cg, TR::InstOpCode::AND, node, tempReg, tempReg, maskReg);

   generateMemSrc1Instruction(cg, TR::InstOpCode::stdx, node, TR::MemoryReference::createWithIndexReg(cg, NULL, outputAddressReg, 8), tempReg);

   /* Update remainingReg, jump to the end if 0. */
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, remainingReg, remainingReg, -4);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, condReg, remainingReg, 0);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, condReg);

   /* 4 bytes were loaded and 8 bytes were stored so bump up inputAddressReg and outputAddressReg by those amounts. */
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, inputAddressReg, inputAddressReg, 4);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, outputAddressReg, outputAddressReg, 8);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, load1Label, NULL);

   /*
    * Unrolled byte by byte section.
    * No more than 3 bytes left to be processed at this point.
    * They are individually loaded, zero extended and stored by as a char.
    */
   generateTrg1MemInstruction(cg, TR::InstOpCode::lbz, node, tempReg, TR::MemoryReference::createWithDisplacement(cg, inputAddressReg, 0, 1));
   generateMemSrc1Instruction(cg, TR::InstOpCode::sth, node, TR::MemoryReference::createWithDisplacement(cg, outputAddressReg, 0, 2), tempReg);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, remainingReg, remainingReg, -1);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, condReg, remainingReg, 0);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, condReg);

   generateTrg1MemInstruction(cg, TR::InstOpCode::lbz, node, tempReg, TR::MemoryReference::createWithDisplacement(cg, inputAddressReg, 1, 1));
   generateMemSrc1Instruction(cg, TR::InstOpCode::sth, node, TR::MemoryReference::createWithDisplacement(cg, outputAddressReg, 2, 2), tempReg);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, remainingReg, remainingReg, -1);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, condReg, remainingReg, 0);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, condReg);

   generateTrg1MemInstruction(cg, TR::InstOpCode::lbz, node, tempReg, TR::MemoryReference::createWithDisplacement(cg, inputAddressReg, 2, 1));
   generateMemSrc1Instruction(cg, TR::InstOpCode::sth, node, TR::MemoryReference::createWithDisplacement(cg, outputAddressReg, 4, 2), tempReg);

   /* Everything is done. */
   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel, dependencies);
   doneLabel->setEndInternalControlFlow();

   dependencies->stopUsingDepRegs(cg);

   for (int32_t i = 0; i < node->getNumChildren(); i++)
      {
      cg->decReferenceCount(node->getChild(i));
      }

   return true;
   }

static TR::Register *inlineStringCodingHasNegativesOrCountPositives(TR::Node *node,
                                                                    TR::CodeGenerator *cg,
                                                                    bool isCountPositives)
   {
   TR::Compilation *comp = cg->comp();
   TR_ASSERT_FATAL(comp->target().is64Bit(), "countPositives is only supported on 64-bit JVMs!");

   bool isLE = comp->target().cpu.isLittleEndian();
   bool p9Plus = cg->comp()->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P9);
   bool p10Plus = cg->comp()->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P10);

   TR::Register *startReg = cg->gprClobberEvaluate(node->getChild(0)); // array
   TR::Register *indexReg = cg->gprClobberEvaluate(node->getChild(1)); // offset
   TR::Register *lengthReg = NULL; // length
   if (p9Plus)
      lengthReg = cg->evaluate(node->getChild(2));
   else // on P8, we will use a weird trick to overwrite length later
      lengthReg = cg->gprClobberEvaluate(node->getChild(2));

   TR::Register *tempReg = cg->allocateRegister();

   TR::Register *cr6 = cg->allocateRegister(TR_CCR);
   TR::Register *cr0 = cg->allocateRegister(TR_CCR);

   TR::Register *vconstant0Reg = cg->allocateRegister(TR_VRF);
   TR::Register *vtmp1Reg = cg->allocateRegister(TR_VRF);

   TR::Register *storeReg = cg->allocateRegister();
   TR::Register *maskReg = cg->allocateRegister();

   TR::LabelSymbol *serialCheckLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *serialHWordLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *serial1Label = generateLabelSymbol(cg);
   TR::LabelSymbol *serialWordCheckLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *serialDWordCheckLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *CheckLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *serialWordLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *serialDWordLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *vectorLoopPrepLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *vectorLoopLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *matchLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *noMatchLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *endLabel = generateLabelSymbol(cg);

   // check empty
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, cr0, lengthReg, 0);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::ble, node, noMatchLabel, cr0);

   // skip over or load the header
#if defined(J9VM_GC_SPARSE_HEAP_ALLOCATION)
   if (TR::Compiler->om.isOffHeapAllocationEnabled())
      {
      generateTrg1MemInstruction(
         cg, TR::InstOpCode::ld, node, startReg,
         TR::MemoryReference::createWithDisplacement(
            cg, startReg, TR::Compiler->om.offsetOfContiguousDataAddrField(), 8)
         );
      }
   else
#endif /* J9VM_GC_SPARSE_HEAP_ALLOCATION */
      {
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, startReg, startReg,
                                     TR::Compiler->om.contiguousArrayHeaderSizeInBytes());
      }

   // --- check first byte
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, cr0, lengthReg, 1);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, serialCheckLabel, cr0);
   generateTrg1MemInstruction(cg, TR::InstOpCode::lbzx, node, storeReg,
         TR::MemoryReference::createWithIndexReg(cg, startReg, indexReg, 1));
   if (p10Plus) // we can use setbc which is faster
      {
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, storeReg, storeReg, 0x80);
      // if the ZERO bit (bit 2) is zero, the value is negative
      if (isCountPositives) // indexReg = storeReg >= 0
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::setbc, node, indexReg, cr0, 2);
      else // indexReg = storeReg < 0
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::setbcr, node, tempReg, cr0, 2);
      }
   else
      {
      if (isCountPositives)
         generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, indexReg, 0);
      else
         generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, tempReg, 1);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, storeReg, storeReg, 0x80);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, endLabel, cr0);
      if (isCountPositives)
         generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, indexReg, 1);
      else
         generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, tempReg, 0);
      }
   generateLabelInstruction(cg, TR::InstOpCode::b, node, endLabel);

   // --- a very quick check with anything longer being checked at the serialWordCheck
   generateLabelInstruction(cg, TR::InstOpCode::label, node, serialCheckLabel);
   // get the starting address
   generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, startReg, startReg, indexReg);
   // make the index 0 since everything we need is relative to the offset
   generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, indexReg, 0);
   // Can we load at least a word?
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, cr0, lengthReg, 4);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bge, node, serialWordCheckLabel, cr0);
   // fallthrough to load half word, since the length can't be 1

   // --- size 2-3: load a half word
   generateLabelInstruction(cg, TR::InstOpCode::label, node, serialHWordLabel);
   // For countPositives, we use load-byte-reverse for LE so that we can count leading zeroes later.
   if (isCountPositives && isLE)
      generateTrg1MemInstruction(cg, TR::InstOpCode::lhbrx, node, storeReg,
            TR::MemoryReference::createWithIndexReg(cg, startReg, indexReg, 2));
   else
      generateTrg1MemInstruction(cg, TR::InstOpCode::lhzx, node, storeReg,
            TR::MemoryReference::createWithIndexReg(cg, startReg, indexReg, 2));
   if (isCountPositives) // when counting positives, we must consider every byte separately
      {
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, storeReg, storeReg, 0x8080);
      generateTrg1Src1Instruction(cg, TR::InstOpCode::cntlzw, node, storeReg, storeReg);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::srawi, node, storeReg, storeReg, 3);
      // ignore the first two bytes in the word
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, storeReg, storeReg, -2);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, indexReg, storeReg, indexReg);
      // existence of negative values means we can jump away
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, endLabel, cr0);
      }
   else // for hasNegatives we don't care about the byte ordering
      {
      generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, tempReg, 1);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, storeReg, storeReg, 0x8080);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, endLabel, cr0);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, indexReg, indexReg, 2);
      }
   // 0-1 item remaining to be checked
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp4, node, cr0, indexReg, lengthReg);
   if (isCountPositives)
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, endLabel, cr0);
   else
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, noMatchLabel, cr0);
   // fallthrough otherwise

   // --- size 1: load byte
   generateLabelInstruction(cg, TR::InstOpCode::label, node, serial1Label);
   generateTrg1MemInstruction(cg, TR::InstOpCode::lbzx, node, storeReg,
         TR::MemoryReference::createWithIndexReg(cg, startReg, indexReg, 1));
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, storeReg, storeReg, 0x80);
   // if the ZERO bit (bit 2) is zero, the value is negative
   if (p10Plus)
      {
      if (isCountPositives) // indexReg = storeReg < 0 ? indexReg : indexReg + 1
         {
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::setbc, node, tempReg, cr0, 2);
         generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, indexReg, indexReg, tempReg);
         }
      else // tempReg = (storeReg < 0)
         {
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::setbcr, node, tempReg, cr0, 2);
         }
      generateLabelInstruction(cg, TR::InstOpCode::b, node, endLabel);
      }
   else
      {
      // For countPosives, we should return the existing index for a negative value, so we can
      // jump straight to the end, otherwise we return the length
      // For hasNegatives, we pre-load a 1 in the case of negatives, and load back a 0 otherwise
      if (!isCountPositives)
         generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, tempReg, 1);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, endLabel, cr0);
      if (isCountPositives)
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, indexReg, indexReg, 1);
      else
         generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, tempReg, 0);
      generateLabelInstruction(cg, TR::InstOpCode::b, node, endLabel);
      }

   // This is actually where we do our main length checks; we don't do it earlier for speed.
   generateLabelInstruction(cg, TR::InstOpCode::label, node, serialWordCheckLabel);
   // tempReg marks the end where we can use the vector loop; go there if that's positive.
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addic_r, node, tempReg, lengthReg, -15);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bgt, node, vectorLoopPrepLabel, cr0);

   // check if we can load double words, but first do some common code
   generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, tempReg, lengthReg);
   generateLabelInstruction(cg, TR::InstOpCode::label, node, serialDWordCheckLabel);
   // we want to load 0x80808080 into maskReg, but lis was designed for signed values,
   // and would throw an error for 0x8080, yet it could accept the equivalent negative value of it;
   // we don't worry about sign extension since the upper word should be 0 in storeReg after lwzx
   generateTrg1ImmInstruction(cg, TR::InstOpCode::lis, node, maskReg, -32640);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::ori, node, maskReg, maskReg, 0x8080);

   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, cr0, tempReg, 8);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bge, node, serialDWordLabel, cr0);

   // --- size 4-7: load one word
   generateLabelInstruction(cg, TR::InstOpCode::label, node, serialWordLabel);
   // For countPositives, we use load-byte-reverse for LE so that we can count leading zeroes later.
   if (isCountPositives && isLE)
      generateTrg1MemInstruction(cg, TR::InstOpCode::lwbrx, node, storeReg,
            TR::MemoryReference::createWithIndexReg(cg, startReg, indexReg, 4));
   else
      generateTrg1MemInstruction(cg, TR::InstOpCode::lwzx, node, storeReg,
            TR::MemoryReference::createWithIndexReg(cg, startReg, indexReg, 4));
   if (isCountPositives) // when counting positives, we must consider every byte separately
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::and_r, node, storeReg, storeReg, maskReg);
      generateTrg1Src1Instruction(cg, TR::InstOpCode::cntlzw, node, storeReg, storeReg);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::srawi, node, storeReg, storeReg, 3);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, indexReg, storeReg, indexReg);
      // existence of negative values means we can jump away
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, endLabel, cr0);
      }
   else
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::and_r, node, storeReg, storeReg, maskReg);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, matchLabel, cr0);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, indexReg, indexReg, 4);
      }
   // If we didn't jump away, we must have 0-3 items remaining to be checked.
   // We kinda have to tuck the code checking which residual handler to use here:
   //    no item - end; 1 item - byte; 2-3 items - hword
   generateTrg1Src2Instruction(cg, TR::InstOpCode::subf_r, node, tempReg, indexReg, lengthReg);
   if (isCountPositives)
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, endLabel, cr0);
   else
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, noMatchLabel, cr0);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, cr0, tempReg, 2);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, serial1Label, cr0);
   generateLabelInstruction(cg, TR::InstOpCode::b, node, serialHWordLabel);

   // --- size 8-15: load double word
   generateLabelInstruction(cg, TR::InstOpCode::label, node, serialDWordLabel);
   // No length check is needed, since we have checked it before coming here

   // maskReg is currently 0x80808080, we want it to have 8 bytes of 0x80 instead
   generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldimi, node, maskReg, maskReg, 32,
         CONSTANT64(0xFFFFFFFF00000000));

   // For countPositives, we use load-byte-reverse for LE so that we can count leading zeroes later.
   if (isCountPositives && isLE)
      generateTrg1MemInstruction(cg, TR::InstOpCode::ldbrx, node, storeReg,
            TR::MemoryReference::createWithIndexReg(cg, startReg, indexReg, 8));
   else
      generateTrg1MemInstruction(cg, TR::InstOpCode::ld, node, storeReg,
            TR::MemoryReference::createWithIndexReg(cg, startReg, indexReg, 8));

   if (isCountPositives) // when counting positives, we must consider every byte separately
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::and_r, node, storeReg, storeReg, maskReg);
      generateTrg1Src1Instruction(cg, TR::InstOpCode::cntlzd, node, storeReg, storeReg);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::srawi, node, storeReg, storeReg, 3);
      generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, indexReg, storeReg, indexReg);
      // existence of negative values means we can jump away
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, endLabel, cr0);
      }
   else
      {
      generateTrg1Src2Instruction(cg, TR::InstOpCode::and_r, node, storeReg, storeReg, maskReg);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, matchLabel, cr0);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, indexReg, indexReg, 8);
      }
   // If we didn't jump away, we have 0-7 items to deal with:
   // We kinda have to tuck the code checking which residual handler to use here:
   //    no item - end; 1 item - byte; 2-3 items - hword; 4-7 items - word.
   generateTrg1Src2Instruction(cg, TR::InstOpCode::subf_r, node, tempReg, indexReg, lengthReg);
   if (isCountPositives)
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, endLabel, cr0);
   else
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, noMatchLabel, cr0);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, cr0, tempReg, 4);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bge, node, serialWordLabel, cr0);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, cr0, tempReg, 2);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bge, node, serialHWordLabel, cr0);
   generateLabelInstruction(cg, TR::InstOpCode::b, node, serial1Label);

   // --- vector facility: size 16 and up
   generateLabelInstruction(cg, TR::InstOpCode::label, node, vectorLoopPrepLabel);
   // ready the zero reg
   generateTrg1Src2Instruction(cg, TR::InstOpCode::vxor, node, vconstant0Reg, vconstant0Reg, vconstant0Reg);

   generateLabelInstruction(cg, TR::InstOpCode::label, node, vectorLoopLabel);
   // Load 16 items; for p9+ we can count zeroes in a vector register, so loading in order matters;
   // before p9, we can't load bytes in the correct order anyways, so we ignore the ordering here.
   if (p9Plus)
      generateTrg1Src2Instruction(cg, TR::InstOpCode::lxvb16x, node, vtmp1Reg, startReg, indexReg);
   else
      generateTrg1Src2Instruction(cg, TR::InstOpCode::lxvw4x, node, vtmp1Reg, startReg, indexReg);
   // bit 2 of cr6 (ZERO) will not be set if any comparison is true
   generateTrg1Src2Instruction(cg, TR::InstOpCode::vcmpgtsb_r, node, vtmp1Reg, vconstant0Reg, vtmp1Reg);
   // branch when the ZERO bit is not set
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, matchLabel, cr6);

   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, indexReg, indexReg, 16);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::cmp4, node, cr0, indexReg, tempReg);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, vectorLoopLabel, cr0);

   // go to residue if we don't have enough items to do one load
   // We kinda have to tuck the code checking which residual handler to use here:
   //    no item - end; 1 item - byte; 2-3 items - hword; 4-15 items - word/dword.
   generateTrg1Src2Instruction(cg, TR::InstOpCode::subf_r, node, tempReg, indexReg, lengthReg);
   if (isCountPositives)
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, endLabel, cr0);
   else
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, noMatchLabel, cr0);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, cr0, tempReg, 4);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bge, node, serialDWordCheckLabel, cr0);
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::cmpi4, node, cr0, tempReg, 2);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bge, node, serialHWordLabel, cr0);
   generateLabelInstruction(cg, TR::InstOpCode::b, node, serial1Label);

   // --- matchLabel is used when we know there is a match
   // For countPositives, this is when a negative value is found in the vtmp1Reg, so
   // we have to find its exact location.
   // For hasNegatives, this can be used anywhere since it simply lets the function return 1.
   generateLabelInstruction(cg, TR::InstOpCode::label, node, matchLabel);
   if (isCountPositives)
      {
      if (p9Plus) // just count for P9+
         {
         generateTrg1Src1Instruction(cg, TR::InstOpCode::vclzlsbb, node, tempReg, vtmp1Reg);
         generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, indexReg, tempReg, indexReg);
         generateLabelInstruction(cg, TR::InstOpCode::b, node, endLabel);
         }
      else // otherwise, we simply go through the last 16 items as if they are the last 16
         {
         // Since there must be a match in the last 16 items, the residue loop
         // will search a[i] to a[i+14] and return if a match is found,
         // and return the 'length' (i+15) if no match is found since the match must be a[i+15].
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, lengthReg, indexReg, 15);
         generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, tempReg, 15);
         generateLabelInstruction(cg, TR::InstOpCode::b, node, serialDWordCheckLabel);
         }
      }
   else // just report 1
      {
      generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, tempReg, 1);
      generateLabelInstruction(cg, TR::InstOpCode::b, node, endLabel);
      }

   // --- load the length for countPositves; load 0 for hasNegative
   generateLabelInstruction(cg, TR::InstOpCode::label, node, noMatchLabel);
   if (isCountPositives)
      generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, indexReg, lengthReg);
   else
      generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, tempReg, 0);
   // end

   TR::RegisterDependencyConditions *deps =
      new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, 10, cg->trMemory());

   deps->addPostCondition(startReg, TR::RealRegister::NoReg);
   deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();

   deps->addPostCondition(indexReg, TR::RealRegister::NoReg);
   deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();

   deps->addPostCondition(lengthReg, TR::RealRegister::NoReg);
   deps->getPostConditions()->getRegisterDependency(deps->getAddCursorForPost() - 1)->setExcludeGPR0();

   deps->addPostCondition(tempReg, TR::RealRegister::NoReg);

   deps->addPostCondition(cr6, TR::RealRegister::cr6);
   deps->addPostCondition(cr0, TR::RealRegister::cr0);

   deps->addPostCondition(vconstant0Reg, TR::RealRegister::NoReg);
   deps->addPostCondition(vtmp1Reg, TR::RealRegister::NoReg);

   deps->addPostCondition(storeReg, TR::RealRegister::NoReg);
   deps->addPostCondition(maskReg, TR::RealRegister::NoReg);

   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, endLabel, deps);

   if (isCountPositives) // if countPositives, indexReg will contain the first negative value
      {
      node->setRegister(indexReg);
      cg->stopUsingRegister(tempReg);
      }
   else // if hasNegative, we will have a tempReg ready with zero or one
      {
      node->setRegister(tempReg);
      cg->stopUsingRegister(indexReg);
      }

   cg->stopUsingRegister(startReg);
   cg->stopUsingRegister(lengthReg);
   cg->stopUsingRegister(cr6);
   cg->stopUsingRegister(cr0);
   cg->stopUsingRegister(vconstant0Reg);
   cg->stopUsingRegister(vtmp1Reg);

   cg->stopUsingRegister(storeReg);
   cg->stopUsingRegister(maskReg);

   for (int32_t i = 0; i < node->getNumChildren(); i++)
      {
      cg->decReferenceCount(node->getChild(i));
      }

   if (isCountPositives) // if countPositives, indexReg will contain the first negative value
      return indexReg;
   return tempReg; // if hasNegative, we will have a tempReg ready with zero or one
   }

/*
 * Arraycopy evaluator needs a version of inlineArrayCopy that can be used inside internal control flow. For this version of inlineArrayCopy, registers must
 * be allocated outside of this function so the dependency at the end of the control flow knows about them.
 */
static void inlineArrayCopy_ICF(TR::Node *node, int64_t byteLen, TR::Register *src, TR::Register *dst, TR::CodeGenerator *cg, TR::Register *cndReg,
                                TR::Register *tmp1Reg, TR::Register *tmp2Reg, TR::Register *tmp3Reg, TR::Register *tmp4Reg,
                                TR::Register *fp1Reg, TR::Register *fp2Reg, TR::Register *fp3Reg, TR::Register *fp4Reg)
   {
   if (byteLen == 0)
      return;

   bool postP10CopyInline = cg->comp()->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P10) &&
                            cg->comp()->target().cpu.supportsFeature(OMR_FEATURE_PPC_HAS_VSX);

   if (postP10CopyInline)
      {
      // tmp1Reg and fp1Reg can be used: fp1Reg is a VSX_VECTOR

      int32_t iteration64 = byteLen >> 6, residue64 = byteLen & 0x3F, standingOffset = 0;

      if (iteration64 > 0)
         {
         TR::LabelSymbol *loopStart;

         if (iteration64 > 1)
            {
            generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, tmp1Reg, iteration64);
            generateSrc1Instruction(cg, TR::InstOpCode::mtctr, node, tmp1Reg);

            loopStart = generateLabelSymbol(cg);
            generateLabelInstruction(cg, TR::InstOpCode::label, node, loopStart);
            }

         generateTrg1MemInstruction(cg, TR::InstOpCode::lxv, node, fp1Reg, TR::MemoryReference::createWithDisplacement(cg, src, 0, 16));
         generateMemSrc1Instruction(cg, TR::InstOpCode::stxv, node, TR::MemoryReference::createWithDisplacement(cg, dst, 0, 16), fp1Reg);
         generateTrg1MemInstruction(cg, TR::InstOpCode::lxv, node, fp1Reg, TR::MemoryReference::createWithDisplacement(cg, src, 16, 16));
         generateMemSrc1Instruction(cg, TR::InstOpCode::stxv, node, TR::MemoryReference::createWithDisplacement(cg, dst, 16, 16), fp1Reg);
         generateTrg1MemInstruction(cg, TR::InstOpCode::lxv, node, fp1Reg, TR::MemoryReference::createWithDisplacement(cg, src, 32, 16));
         generateMemSrc1Instruction(cg, TR::InstOpCode::stxv, node, TR::MemoryReference::createWithDisplacement(cg, dst, 32, 16), fp1Reg);
         generateTrg1MemInstruction(cg, TR::InstOpCode::lxv, node, fp1Reg, TR::MemoryReference::createWithDisplacement(cg, src, 48, 16));
         generateMemSrc1Instruction(cg, TR::InstOpCode::stxv, node, TR::MemoryReference::createWithDisplacement(cg, dst, 48, 16), fp1Reg);

         if (iteration64 > 1)
            {
            generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi2, node, src, src, 64);
            generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi2, node, dst, dst, 64);
            generateConditionalBranchInstruction(cg, TR::InstOpCode::bdnz, node, loopStart, cndReg);
            }
         else
            standingOffset = 64;
         }

      for (int32_t i = 0; i < (residue64>>4); i++)
         {
         generateTrg1MemInstruction(cg, TR::InstOpCode::lxv, node, fp1Reg, TR::MemoryReference::createWithDisplacement(cg, src, standingOffset+i*16, 16));
         generateMemSrc1Instruction(cg, TR::InstOpCode::stxv, node, TR::MemoryReference::createWithDisplacement(cg, dst, standingOffset+i*16, 16), fp1Reg);
         }

      if ((residue64 & 0xF) != 0)
         {
         standingOffset += residue64 & 0x30;
         switch (residue64 & 0xF)
            {
            case 1:
               generateTrg1MemInstruction(cg, TR::InstOpCode::lbz, node, tmp1Reg, TR::MemoryReference::createWithDisplacement(cg, src, standingOffset, 1));
               generateMemSrc1Instruction(cg, TR::InstOpCode::stb, node, TR::MemoryReference::createWithDisplacement(cg, dst, standingOffset, 1), tmp1Reg);
               break;
            case 2:
               generateTrg1MemInstruction(cg, TR::InstOpCode::lhz, node, tmp1Reg, TR::MemoryReference::createWithDisplacement(cg, src, standingOffset, 2));
               generateMemSrc1Instruction(cg, TR::InstOpCode::sth, node, TR::MemoryReference::createWithDisplacement(cg, dst, standingOffset, 2), tmp1Reg);
               break;
            case 4:
               generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, tmp1Reg, TR::MemoryReference::createWithDisplacement(cg, src, standingOffset, 4));
               generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, dst, standingOffset, 4), tmp1Reg);
               break;
            case 8:
               generateTrg1MemInstruction(cg, TR::InstOpCode::ld, node, tmp1Reg, TR::MemoryReference::createWithDisplacement(cg, src, standingOffset, 8));
               generateMemSrc1Instruction(cg, TR::InstOpCode::std, node, TR::MemoryReference::createWithDisplacement(cg, dst, standingOffset, 8), tmp1Reg);
               break;
            default:
               generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, tmp1Reg, residue64 & 0xF);
               generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldicr, node, tmp1Reg, tmp1Reg,
                                               56, CONSTANT64(0xff00000000000000));
               if (standingOffset != 0)
                  {
                  generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi2, node, src, src, standingOffset);
                  generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi2, node, dst, dst, standingOffset);
                  }
               generateTrg1Src2Instruction(cg, TR::InstOpCode::lxvl, node, fp1Reg, src, tmp1Reg);
               generateSrc3Instruction(cg, TR::InstOpCode::stxvl, node, fp1Reg, dst, tmp1Reg);
               break;
            }
         }
      }
   else
      {
      TR::Register *regs[4] = {tmp1Reg, tmp2Reg, tmp3Reg, tmp4Reg};
      TR::Register *fpRegs[4] = {fp1Reg, fp2Reg, fp3Reg, fp4Reg};
      int32_t groups, residual;

      static bool disableLEArrayCopyInline = (feGetEnv("TR_disableLEArrayCopyInline") != NULL);
      bool supportsLEArrayCopyInline = cg->comp()->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P8) && !disableLEArrayCopyInline && cg->comp()->target().cpu.isLittleEndian() && cg->comp()->target().cpu.hasFPU() && cg->comp()->target().is64Bit();

      if (cg->comp()->target().is64Bit())
         {
         groups = byteLen >> 5;
         residual = byteLen & 0x0000001F;
         }
      else
         {
         groups = byteLen >> 4;
         residual = byteLen & 0x0000000F;
         }

      int32_t regIx = 0, ix = 0, fpRegIx = 0;
      int32_t memRefSize;
      TR::InstOpCode::Mnemonic load, store;
      TR::Compilation* comp = cg->comp();

      /* Some machines have issues with 64bit loads/stores in 32bit mode, ie. sicily, do not check for is64BitProcessor() */
      memRefSize = TR::Compiler->om.sizeofReferenceAddress();
      load = TR::InstOpCode::Op_load;
      store = TR::InstOpCode::Op_st;

      if (groups != 0)
         {
         TR::LabelSymbol *loopStart;

         if (groups != 1)
            {
            if (groups <= UPPER_IMMED)
               generateTrg1ImmInstruction(cg, TR::InstOpCode::li, node, regs[0], groups);
            else
               loadConstant(cg, node, groups, regs[0]);
            generateSrc1Instruction(cg, TR::InstOpCode::mtctr, node, regs[0]);

            loopStart = generateLabelSymbol(cg);
            generateLabelInstruction(cg, TR::InstOpCode::label, node, loopStart);
            }

         if (supportsLEArrayCopyInline)
            {
            generateTrg1MemInstruction(cg, TR::InstOpCode::lfd, node, fpRegs[3], TR::MemoryReference::createWithDisplacement(cg, src,            0, memRefSize));
            generateTrg1MemInstruction(cg, TR::InstOpCode::lfd, node, fpRegs[2], TR::MemoryReference::createWithDisplacement(cg, src,   memRefSize, memRefSize));
            generateTrg1MemInstruction(cg, TR::InstOpCode::lfd, node, fpRegs[1], TR::MemoryReference::createWithDisplacement(cg, src, 2*memRefSize, memRefSize));
            generateTrg1MemInstruction(cg, TR::InstOpCode::lfd, node, fpRegs[0], TR::MemoryReference::createWithDisplacement(cg, src, 3*memRefSize, memRefSize));
            }
         else
            {
            generateTrg1MemInstruction(cg, load, node, regs[3], TR::MemoryReference::createWithDisplacement(cg, src,            0, memRefSize));
            generateTrg1MemInstruction(cg, load, node, regs[2], TR::MemoryReference::createWithDisplacement(cg, src,   memRefSize, memRefSize));
            generateTrg1MemInstruction(cg, load, node, regs[1], TR::MemoryReference::createWithDisplacement(cg, src, 2*memRefSize, memRefSize));
            generateTrg1MemInstruction(cg, load, node, regs[0], TR::MemoryReference::createWithDisplacement(cg, src, 3*memRefSize, memRefSize));
            }

         if (groups != 1)
            generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, src, src, 4*memRefSize);

         if (supportsLEArrayCopyInline)
            {
            generateMemSrc1Instruction(cg, TR::InstOpCode::stfd, node, TR::MemoryReference::createWithDisplacement(cg, dst,            0, memRefSize), fpRegs[3]);
            generateMemSrc1Instruction(cg, TR::InstOpCode::stfd, node, TR::MemoryReference::createWithDisplacement(cg, dst,   memRefSize, memRefSize), fpRegs[2]);
            generateMemSrc1Instruction(cg, TR::InstOpCode::stfd, node, TR::MemoryReference::createWithDisplacement(cg, dst, 2*memRefSize, memRefSize), fpRegs[1]);
            generateMemSrc1Instruction(cg, TR::InstOpCode::stfd, node, TR::MemoryReference::createWithDisplacement(cg, dst, 3*memRefSize, memRefSize), fpRegs[0]);
            }
         else
            {
            generateMemSrc1Instruction(cg, store, node, TR::MemoryReference::createWithDisplacement(cg, dst,            0, memRefSize), regs[3]);
            generateMemSrc1Instruction(cg, store, node, TR::MemoryReference::createWithDisplacement(cg, dst,   memRefSize, memRefSize), regs[2]);
            generateMemSrc1Instruction(cg, store, node, TR::MemoryReference::createWithDisplacement(cg, dst, 2*memRefSize, memRefSize), regs[1]);
            generateMemSrc1Instruction(cg, store, node, TR::MemoryReference::createWithDisplacement(cg, dst, 3*memRefSize, memRefSize), regs[0]);
            }

         if (groups != 1)
            {
            generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi2, node, dst, dst, 4*memRefSize);
            generateConditionalBranchInstruction(cg, TR::InstOpCode::bdnz, node, loopStart, cndReg);
            }
         else
            {
            ix = 4*memRefSize;
            }
         }

      for (; residual>=memRefSize; residual-=memRefSize, ix+=memRefSize)
         {
         if (supportsLEArrayCopyInline)
            {
            TR::Register *oneReg = fpRegs[fpRegIx++];
            if (fpRegIx>3 || fpRegs[fpRegIx]==NULL)
               fpRegIx = 0;
            generateTrg1MemInstruction(cg, TR::InstOpCode::lfd, node, oneReg, TR::MemoryReference::createWithDisplacement(cg, src, ix, memRefSize));
            generateMemSrc1Instruction(cg, TR::InstOpCode::stfd, node, TR::MemoryReference::createWithDisplacement(cg, dst, ix, memRefSize), oneReg);
            }
         else
            {
            TR::Register *oneReg = regs[regIx++];
            if (regIx>3 || regs[regIx]==NULL)
               regIx = 0;
            generateTrg1MemInstruction(cg, load, node, oneReg, TR::MemoryReference::createWithDisplacement(cg, src, ix, memRefSize));
            generateMemSrc1Instruction(cg, store, node, TR::MemoryReference::createWithDisplacement(cg, dst, ix, memRefSize), oneReg);
            }
         }

      if (residual != 0)
         {
         if (residual & 4)
            {
            if (supportsLEArrayCopyInline)
               {
               TR::Register *oneReg = fpRegs[fpRegIx++];
               if (fpRegIx>3 || fpRegs[fpRegIx]==NULL)
                  fpRegIx = 0;
               generateTrg1MemInstruction(cg, TR::InstOpCode::lfs, node, oneReg, TR::MemoryReference::createWithDisplacement(cg, src, ix, 4));
               generateMemSrc1Instruction(cg, TR::InstOpCode::stfs, node, TR::MemoryReference::createWithDisplacement(cg, dst, ix, 4), oneReg);
               }
            else
               {
               TR::Register *oneReg = regs[regIx++];
               if (regIx>3 || regs[regIx]==NULL)
                  regIx = 0;
               generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, oneReg, TR::MemoryReference::createWithDisplacement(cg, src, ix, 4));
               generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, dst, ix, 4), oneReg);
               }
            ix += 4;
            }
         if (residual & 2)
            {
            TR::Register *oneReg = regs[regIx++];
            if (regIx>3 || regs[regIx]==NULL)
               regIx = 0;
            generateTrg1MemInstruction(cg, TR::InstOpCode::lhz, node, oneReg, TR::MemoryReference::createWithDisplacement(cg, src, ix, 2));
            generateMemSrc1Instruction(cg, TR::InstOpCode::sth, node, TR::MemoryReference::createWithDisplacement(cg, dst, ix, 2), oneReg);
            ix += 2;
            }
         if (residual & 1)
            {
            generateTrg1MemInstruction(cg, TR::InstOpCode::lbz, node, regs[regIx], TR::MemoryReference::createWithDisplacement(cg, src, ix, 1));
            generateMemSrc1Instruction(cg, TR::InstOpCode::stb, node, TR::MemoryReference::createWithDisplacement(cg, dst, ix, 1), regs[regIx]);
            }
         }
      }

   return;
   }

bool
J9::Power::CodeGenerator::inlineDirectCall(TR::Node *node, TR::Register *&resultReg)
   {
   TR::CodeGenerator *cg = self();
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *)(comp->fe());
   TR::MethodSymbol * methodSymbol = node->getSymbol()->getMethodSymbol();

   static bool disableDCAS = (feGetEnv("TR_DisablePPCDCAS") != NULL);
   static bool useJapaneseCompression = (feGetEnv("TR_JapaneseComp") != NULL);

   if (comp->getSymRefTab()->isNonHelper(node->getSymbolReference(), TR::SymbolReferenceTable::singlePrecisionSQRTSymbol))
      {
      resultReg = inlineSinglePrecisionFP(node, TR::InstOpCode::fsqrts, cg);
      return true;
      }
   else if (OMR::CodeGeneratorConnector::inlineDirectCall(node, resultReg))
      {
      return true;
      }
   else if (methodSymbol)
      {
      bool disableCASInlining = !cg->getSupportsInlineUnsafeCompareAndSet();
      bool disableCAEInlining = !cg->getSupportsInlineUnsafeCompareAndExchange();
      static bool disableOSW = feGetEnv("TR_noPauseOnSpinWait") != NULL;
      switch (methodSymbol->getRecognizedMethod())
         {
      case TR::java_lang_Thread_onSpinWait:
         {
         if (!disableOSW)
            {
            if (comp->getOption(TR_TraceCG))
               {
               traceMsg(comp, "Inlining Thread.onSpinWait call node %p as yield instruction.\n", node);
               }

            /*
             * onSpinWait() method calls VM_AtomicSupport::yieldCPU() which is a yield instruction encoded as "or r27, r27, r27".
             * Check omr/include_core/AtomicSupport.hpp for the JNI implementation.
             */
            generateInstruction(cg, TR::InstOpCode::yield, node);
            return true;
            }
         break;
         }
      case TR::java_util_concurrent_ConcurrentLinkedQueue_tmOffer:
         {
         if (cg->getSupportsInlineConcurrentLinkedQueue())
            {
            resultReg = inlineConcurrentLinkedQueueTMOffer(node, cg);
            return true;
            }
         break;
         }

      case TR::java_util_concurrent_ConcurrentLinkedQueue_tmPoll:
         {
         if (cg->getSupportsInlineConcurrentLinkedQueue())
            {
            resultReg = inlineConcurrentLinkedQueueTMPoll(node, cg);
            return true;
            }
         break;
         }

      case TR::sun_misc_Unsafe_setMemory:
         if (comp->canTransformUnsafeSetMemory())
            {
            TR::Node *dest = node->getChild(1);
            TR::Node *destOffset = node->getChild(2);
            TR::Node *len = node->getChild(3);
            TR::Node *byteValue = node->getChild(4);

            // When using balanced GC policy with offheap allocation enabled, there are three possible cases:
            //
            // 1.) The object at dest is known to be a non-array object at compile time. In this scenario, the final destination address
            //     can be calculated right away as dest += destOffset. The IL node passed in to setmemoryEvaluator will have three
            //     children: dest, len, and byteValue.
            // 2.) The object at dest is known to be an array at compile time. In this scenario, we must load dataAddr from dest
            //     to account for the way arrays are allocated when offheap is enabled. Once this adjustment is made, the final
            //     destination address can be calculated dest += destOffset. Similar to case (1), the IL node passed in to
            //     setmemoryEvaluator() will have three children: dest, len, and byteValue.
            // 3.) The type of the object at dest is unknown at compile time. In this scenario, a runtime array check must be generated to
            //     determine whether dataAddr needs to be loaded before calculating the final destination address. This means that we
            //     cannot determine the final destination address right away, and thus need to keep dest and destOffset separate. In this
            //     case, the node passed in to setmemoryEvaluator() will have four children: dest, destOffset, len, and byteValue.
            //
            // Note that if the default (gencon) GC policy is being used OR if the object is a NULL reference, dataAddr will never need
            // to be loaded no matter the type of the object at dest, so all of the above cases will be treated like case (1).

            //if offheap is not enabled OR if dest is known to be a NULL reference at compile time, default to case (1):
            // - do NOT to load dataAddr to calculate final destination address
            // - calculate the final destination address right away and only pass three children into setmemoryEvaluator()
            bool loadDataAddr = false;
            bool separateDestAndOffset = false;

         #if defined(J9VM_GC_SPARSE_HEAP_ALLOCATION)
            if (TR::Compiler->om.isOffHeapAllocationEnabled() && comp->target().is64Bit() && (!dest->isNull()))
               {
               if (dest->getSymbolReference() != NULL)
                  {
                  //check dstBaseAddrNode type at compile time
                  int length;
                  const char *objTypeSig = dest->getSymbolReference()->getTypeSignature(length);

                  // There are four cases where we cannot be sure of the Object type at compile time:
                  // 1.) The object's type signature is unavailable/unknown
                  // 2.) The signature names java/lang/Object
                  // 3.) The signature names some interface type, wich is a problem because interface types are
                  //     not checked by the verifier
                  // 4.) The signature belongs to a parameter of the calling method, which is a problem because
                  //     parameters can share slots with variables of other, potentially incompatible types
                  bool objTypeUnknown;
                  TR_OpaqueClassBlock *objClass = NULL;

                  if (objTypeSig == NULL)
                     {
                     objTypeUnknown = true;
                     }
                  else
                     {
                     objClass = fej9->getClassFromSignature(objTypeSig, length, dest->getSymbolReference()->getOwningMethod(comp));
                     bool isParameter = dest->getSymbolReference()->getSymbol()->isParm();

                     objTypeUnknown = objClass == NULL || objClass == comp->getObjectClassPointer() ||
                                      TR::Compiler->cls.isInterfaceClass(comp, objClass) || isParameter;
                     }

                  //case (2): want to load dataAddr and calculate final dest address right away if both of the following conditions are met:
                  //          1.) Object is known to be an array at compile time
                  //          2.) Object is known to be a non-NULL reference at compile time
                  loadDataAddr = (!objTypeUnknown) && (objTypeSig[0] == '[') && dest->isNonNull();

                  //case (3): want to keep dest and destOffset separate so that we can perform runtime NULL/array tests if at least one of
                  //          the following conditions are met:
                  //          1.) Object type is unknown at compile time
                  //          2.) Object is known to be an array at compile time AND
                  //              it is NOT known that the object is a non-NULL reference at compile time
                  separateDestAndOffset = objTypeUnknown || ((objTypeSig[0] == '[') && (!dest->isNonNull()));
                  }
               else
                  {
                  //if for some reason we cannot get the symbol reference (and thus, the type signature)
                  //of dest, we'll need to take the conservative approach and pass dest and destOffset to
                  //setmemoryEvaluator() separately
                  separateDestAndOffset = true;
                  }
            }
         #endif /* J9VM_GC_SPARSE_HEAP_ALLOCATION */

            TR::Node *copyMemNode;

            //setting up IL trees to be passed along to setmemoryEvaluator()
            if (separateDestAndOffset) // CASE (3): dest and destOffset passed in separately
               {
               //four child setup
               copyMemNode = TR::Node::createWithSymRef(TR::arrayset, 4, 4, dest, destOffset, len, byteValue, node->getSymbolReference());
               }
            else // CASE (1) and (2): dest += destoffset, then pass in to evaluator
               {
            #if defined(J9VM_GC_SPARSE_HEAP_ALLOCATION)

               if (loadDataAddr) // CASE (2) only
                  {
                  //load dataAddr and use as object base address (previously dest)
                  TR::Node *dataAddrNode = J9::TransformUtil::generateDataAddrLoadTrees(comp, dest);
                  dest = dataAddrNode;
                  }

            #endif /* J9VM_GC_SPARSE_HEAP_ALLOCATION */

               //destOffset is a long, so on 32 bit systems we need a conversion before we can add it to dest
               if (comp->target().is32Bit())
                  destOffset = TR::Node::create(TR::l2i, 1, destOffset);

               //three child setup
               dest = TR::Node::create(comp->target().is32Bit() ? TR::aiadd : TR::aladd, 2, dest, destOffset); //dest += destOffset
               copyMemNode = TR::Node::createWithSymRef(TR::arrayset, 3, 3, dest, len, byteValue, node->getSymbolReference());
               }

            copyMemNode->setByteCodeInfo(node->getByteCodeInfo());
            TR::TreeEvaluator::setmemoryEvaluator(copyMemNode, cg);

            if (node->getChild(0)->getRegister())
               cg->decReferenceCount(node->getChild(0));
            else
               node->getChild(0)->recursivelyDecReferenceCount();

            cg->decReferenceCount(node->getChild(1));
            cg->decReferenceCount(node->getChild(2));
            cg->decReferenceCount(node->getChild(3));
            cg->decReferenceCount(node->getChild(4));
            return true;
            }
         break;

      case TR::java_util_concurrent_atomic_AtomicBoolean_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicInteger_getAndAdd:
      case TR::java_util_concurrent_atomic_AtomicInteger_getAndIncrement:
      case TR::java_util_concurrent_atomic_AtomicInteger_getAndDecrement:
      case TR::java_util_concurrent_atomic_AtomicInteger_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicInteger_addAndGet:
      case TR::java_util_concurrent_atomic_AtomicInteger_incrementAndGet:
      case TR::java_util_concurrent_atomic_AtomicInteger_decrementAndGet:
         {
         resultReg = inlineAtomicOps(node, cg, 4, methodSymbol, false);
         return true;
         break;
         }

      case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndAdd:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndIncrement:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndDecrement:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_getAndSet:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_addAndGet:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_incrementAndGet:
      case TR::java_util_concurrent_atomic_AtomicIntegerArray_decrementAndGet:
         {
         resultReg = inlineAtomicOps(node, cg, 4, methodSymbol, true);
         return true;
         break;
         }

      case TR::java_lang_Math_ceil:
      case TR::java_lang_StrictMath_ceil:
         if (methodSymbol->getResolvedMethodSymbol()->canReplaceWithHWInstr())
            {
            resultReg = inlineDoublePrecisionFP(node, TR::InstOpCode::frip, cg);
            return true;
            }
         break;

      case TR::java_lang_Math_floor:
      case TR::java_lang_StrictMath_floor:
         if (methodSymbol->getResolvedMethodSymbol()->canReplaceWithHWInstr())
            {
            resultReg = inlineDoublePrecisionFP(node, TR::InstOpCode::frim, cg);
            return true;
            }
         break;

      case TR::java_lang_Math_copySign_F:
         // StrictMath copySign spec not guaranteed by fcpsgn instruction
         if (methodSymbol->getResolvedMethodSymbol()->canReplaceWithHWInstr())
            {
            resultReg = inlineSinglePrecisionFPTrg1Src2(node, TR::InstOpCode::fcpsgn, cg);
            return true;
            }
         break;

      case TR::java_lang_Math_copySign_D:
         // StrictMath copySign spec not guaranteed by fcpsgn instruction
         if (methodSymbol->getResolvedMethodSymbol()->canReplaceWithHWInstr())
            {
            resultReg = inlineDoublePrecisionFPTrg1Src2(node, TR::InstOpCode::fcpsgn, cg);
            return true;
            }
         break;

      case TR::java_lang_Math_fma_D:
      case TR::java_lang_StrictMath_fma_D:
         resultReg = inlineFPTrg1Src3(node, TR::InstOpCode::fmadd, cg);
         return true;

      case TR::java_lang_Math_fma_F:
      case TR::java_lang_StrictMath_fma_F:
         resultReg = inlineFPTrg1Src3(node, TR::InstOpCode::fmadds, cg);
         return true;

      case TR::java_lang_String_hashCodeImplCompressed:
      case TR::java_lang_String_hashCodeImplDecompressed:
         if (cg->getSupportsInlineStringHashCode())
            {
            resultReg = inlineStringHashCode(node, cg,
               methodSymbol->getRecognizedMethod() == TR::java_lang_String_hashCodeImplCompressed);
            return resultReg != NULL;
            }
         break;

      case TR::jdk_internal_util_ArraysSupport_vectorizedHashCode:
         if (cg->getSupportsInlineVectorizedHashCode())
            {
            resultReg = inlineVectorizedHashCode(node, cg);
            return resultReg != NULL;
            }
         break;

      case TR::com_ibm_jit_JITHelpers_transformedEncodeUTF16Big:
      case TR::com_ibm_jit_JITHelpers_transformedEncodeUTF16Little:
         if (comp->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P7) && comp->target().cpu.supportsFeature(OMR_FEATURE_PPC_HAS_VSX))
            {
            resultReg = inlineEncodeUTF16(node, cg);
            return true;
            }
         break;

      case TR::java_lang_StringLatin1_indexOfChar:
      case TR::java_lang_StringUTF16_indexOfCharUnsafe:
      case TR::com_ibm_jit_JITHelpers_intrinsicIndexOfLatin1:
      case TR::com_ibm_jit_JITHelpers_intrinsicIndexOfUTF16:
         if (cg->getSupportsInlineStringIndexOf())
            {
            bool isLatin1 = (methodSymbol->getRecognizedMethod() == TR::com_ibm_jit_JITHelpers_intrinsicIndexOfLatin1) ||
                            (methodSymbol->getRecognizedMethod() == TR::java_lang_StringLatin1_indexOfChar);
            if (comp->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P10))
               resultReg = inlineIntrinsicIndexOf_P10(node, cg, isLatin1);
            else
               resultReg = inlineIntrinsicIndexOf(node, cg, isLatin1);
            return resultReg != nullptr;
            }
         break;

      case TR::java_lang_StringLatin1_indexOf:
         if (cg->getSupportsInlineStringIndexOfString())
            {
            resultReg = inlineIntrinsicIndexOfString(node, cg, true);
            return resultReg != nullptr;
            }
         break;

      case TR::java_lang_StringLatin1_inflate_BICII:
         if (cg->getSupportsInlineStringLatin1Inflate())
            {
            bool result = inlineIntrinsicInflate(node, cg);
            if (result)
               {
               resultReg = nullptr;
               }
            return result;
            }
         break;

      case TR::java_lang_StringCoding_hasNegatives:
         if (cg->getSupportsInlineStringCodingHasNegatives())
            {
            resultReg = inlineStringCodingHasNegativesOrCountPositives(node, cg, false);
            return true;
            }
         break;
      case TR::java_lang_StringCoding_countPositives:
         if (cg->getSupportsInlineStringCodingCountPositives())
            {
            resultReg = inlineStringCodingHasNegativesOrCountPositives(node, cg, true);
            return true;
            }
         break;

      case TR::sun_misc_Unsafe_compareAndSwapInt_jlObjectJII_Z:
        // In Java9 this can be either the jdk.internal JNI method or the sun.misc Java wrapper.
        // In Java8 it will be sun.misc which will contain the JNI directly.
        // We only want to inline the JNI methods, so add an explicit test for isNative().
        if (!methodSymbol->isNative())
           break;

        if ((node->isUnsafeGetPutCASCallOnNonArray() || !TR::Compiler->om.canGenerateArraylets()) && node->isSafeForCGToFastPathUnsafeCall())
            {
            if (!disableCASInlining)
               {
               resultReg = VMinlineCompareAndSetOrExchange(node, cg, 4, false);
               return true;
               }
            }
         break;

      case TR::sun_misc_Unsafe_compareAndSwapLong_jlObjectJJJ_Z:
         if (comp->getOption(TR_TraceCG))
            traceMsg(comp, "In evaluator for compareAndSwapLong. node = %p node->isSafeForCGToFastPathUnsafeCall = %p\n", node, node->isSafeForCGToFastPathUnsafeCall());
         // As above, we only want to inline the JNI methods, so add an explicit test for isNative()
         if (!methodSymbol->isNative())
            break;

         if (comp->target().is64Bit() && (node->isUnsafeGetPutCASCallOnNonArray() || !TR::Compiler->om.canGenerateArraylets()) && node->isSafeForCGToFastPathUnsafeCall())
            {
            if (!disableCASInlining)
               {
               resultReg = VMinlineCompareAndSetOrExchange(node, cg, 8, false);
               return true;
               }
            }
         else if ((node->isUnsafeGetPutCASCallOnNonArray() || !TR::Compiler->om.canGenerateArraylets()) && node->isSafeForCGToFastPathUnsafeCall())
            {
            if (!disableCASInlining)
               {
               resultReg = inlineAtomicOperation(node, cg, methodSymbol);
               return true;
               }
            }
         break;

      case TR::sun_misc_Unsafe_compareAndSwapObject_jlObjectJjlObjectjlObject_Z:
         // As above, we only want to inline the JNI methods, so add an explicit test for isNative()
         if (!methodSymbol->isNative())
            break;

         if ((node->isUnsafeGetPutCASCallOnNonArray() || !TR::Compiler->om.canGenerateArraylets()) && node->isSafeForCGToFastPathUnsafeCall())
            {
            if (!disableCASInlining)
               {
               resultReg = VMinlineCompareAndSetOrExchangeReference(node, cg, false);
               return true;
               }
            }
         break;

      case TR::jdk_internal_misc_Unsafe_compareAndExchangeInt:
        if ((node->isUnsafeGetPutCASCallOnNonArray() || !TR::Compiler->om.canGenerateArraylets()) && node->isSafeForCGToFastPathUnsafeCall())
            {
            if (!disableCAEInlining)
               {
               resultReg = VMinlineCompareAndSetOrExchange(node, cg, 4, true);
               return true;
               }
            }
         break;

      case TR::jdk_internal_misc_Unsafe_compareAndExchangeLong:
        if (comp->target().is64Bit() && (node->isUnsafeGetPutCASCallOnNonArray() || !TR::Compiler->om.canGenerateArraylets()) && node->isSafeForCGToFastPathUnsafeCall())
            {
            if (!disableCAEInlining)
               {
               resultReg = VMinlineCompareAndSetOrExchange(node, cg, 8, true);
               return true;
               }
            }
         break;

      case TR::jdk_internal_misc_Unsafe_compareAndExchangeObject:
         /*
          * Starting from Java 12, compareAndExchangeObject was changed from a native call to a
          * Java wrapper calling compareAndExchangeReference.
          * We only want to inline the JNI native method, so add an explicit test for isNative().
          */
         if (!methodSymbol->isNative())
            break;
         /* If native, fall through. */
      case TR::jdk_internal_misc_Unsafe_compareAndExchangeReference:
         if ((node->isUnsafeGetPutCASCallOnNonArray() || !TR::Compiler->om.canGenerateArraylets()) && node->isSafeForCGToFastPathUnsafeCall())
            {
            if (!disableCAEInlining)
               {
               resultReg = VMinlineCompareAndSetOrExchangeReference(node, cg, true);
               return true;
               }
            }
         break;

      case TR::java_nio_Bits_keepAlive:
      case TR::java_lang_ref_Reference_reachabilityFence:
         {
         TR::Node *paramNode = node->getFirstChild();
         TR::Register *paramReg = cg->evaluate(paramNode);
         TR::RegisterDependencyConditions *conditions = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(1, 1, cg->trMemory());
         TR::addDependency(conditions, paramReg, TR::RealRegister::NoReg, TR_GPR, cg);
         TR::LabelSymbol *label = TR::LabelSymbol::create(cg->trHeapMemory(),cg);
         generateDepLabelInstruction(cg, TR::InstOpCode::label, node, label, conditions);
         cg->decReferenceCount(paramNode);
         resultReg = NULL;
         return true;
         }

      case TR::x10JITHelpers_getCPU:
         break;

      case TR::java_lang_Class_isAssignableFrom:
         {
         // Do not use an inline class check if the 'this' Class object is known to be an
         // abstract class or an interface at compile-time (the check will always fail
         // and go out of line).
         //
         TR::Node *receiverNode = node->getFirstChild();
         if (receiverNode->getOpCodeValue() == TR::aloadi &&
             receiverNode->getSymbolReference() == comp->getSymRefTab()->findJavaLangClassFromClassSymbolRef() &&
             receiverNode->getFirstChild()->getOpCodeValue() == TR::loadaddr)
            {
            TR::SymbolReference *receiverClassSymRef = receiverNode->getFirstChild()->getSymbolReference();
            if (receiverClassSymRef->isClassInterface(comp) ||
                receiverClassSymRef->isClassAbstract(comp))
               break;
            }

         static bool disable = feGetEnv("TR_disableInlineIsAssignableFrom") != NULL;
         if (!disable &&
             performTransformation(comp, "O^O PPC Evaluator: Specialize call to java/lang/Class.isAssignableFrom [%p].\n", node))
            {
            resultReg = inlineIsAssignableFrom(node, cg);
            return true;
            }

         break;
         }

      default:
         break;
         }
      }

#ifdef J9VM_OPT_JAVA_CRYPTO_ACCELERATION
   if (self()->inlineCryptoMethod(node, resultReg))
      {
      return true;
      }
#endif

   // No method specialization was done.
   //
   resultReg = NULL;
   return false;
   }

void VMgenerateCatchBlockBBStartPrologue(TR::Node *node, TR::Instruction *fenceInstruction, TR::CodeGenerator *cg)
   {
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (comp->fe());

   TR::Block *block = node->getBlock();

   if (fej9->shouldPerformEDO(block, comp))
      {
      TR::Register *biAddrReg = cg->allocateRegister();
      TR::Register *recompCounterReg = cg->allocateRegister();
      intptr_t addr = (intptr_t) (comp->getRecompilationInfo()->getCounterAddress());
      TR::Instruction *cursor = loadAddressConstant(cg, cg->needRelocationsForBodyInfoData(), node, addr, biAddrReg, NULL, false, TR_BodyInfoAddressLoad);
      TR::MemoryReference *loadbiMR = TR::MemoryReference::createWithDisplacement(cg, biAddrReg, 0, TR::Compiler->om.sizeofReferenceAddress());
      TR::MemoryReference *storebiMR = TR::MemoryReference::createWithDisplacement(cg, biAddrReg, 0, TR::Compiler->om.sizeofReferenceAddress());
      cursor = generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, recompCounterReg, loadbiMR);
      TR::Register *cndRegister = cg->allocateRegister(TR_CCR);
      cursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi2_r, node, recompCounterReg, recompCounterReg, cndRegister, -1, cursor);
      cursor = generateMemSrc1Instruction(cg,TR::InstOpCode::Op_st, node, storebiMR, recompCounterReg, cursor);
      cg->stopUsingRegister(biAddrReg);
      cg->stopUsingRegister(recompCounterReg);

      TR::LabelSymbol *snippetLabel = NULL;
      TR::LabelSymbol *doneLabel = generateLabelSymbol(cg);
      // snippet call chain through jitCallCFunction kills 3 parameter
      // registers that we will reserve here
      TR::Register *arg1Reg, *arg2Reg, *arg3Reg;
      arg1Reg = cg->allocateRegister();
      arg2Reg = cg->allocateRegister();
      arg3Reg = cg->allocateRegister();

      snippetLabel = cg->lookUpSnippet(TR::Snippet::IsForceRecompilation, NULL);
      if (snippetLabel == NULL)
         {
         TR::Snippet *snippet;
         snippetLabel = generateLabelSymbol(cg);
         snippet = new (cg->trHeapMemory()) TR::PPCForceRecompilationSnippet(snippetLabel, doneLabel, cursor, cg);
         cg->addSnippet(snippet);
         TR_ASSERT_FATAL(cg->comp()->getRecompilationInfo(), "Recompilation info should be available");
         cg->comp()->getRecompilationInfo()->getJittedBodyInfo()->setHasEdoSnippet();
         }

      TR::RegisterDependencyConditions *conditions = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(4, 4, cg->trMemory());
      TR::addDependency(conditions, arg1Reg, TR::RealRegister::gr3, TR_GPR, cg);
      TR::addDependency(conditions, arg2Reg, TR::RealRegister::gr4, TR_GPR, cg);
      TR::addDependency(conditions, arg3Reg, TR::RealRegister::gr5, TR_GPR, cg);
      TR::addDependency(conditions, cndRegister, TR::RealRegister::cr0, TR_CCR, cg);

      // used to be blel
      // only need to call snippet once, so using beql instead
      cursor = generateConditionalBranchInstruction(cg, TR::InstOpCode::beql, node, snippetLabel, cndRegister);
      generateDepLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel, conditions);

      cg->machine()->setLinkRegisterKilled(true);
      conditions->stopUsingDepRegs(cg);
      }
   }

TR::Instruction *loadAddressRAM32(TR::CodeGenerator *cg, TR::Node * node, int32_t value, TR::Register *trgReg)
   {
   // load a 32-bit constant into a register with a fixed 2 instruction sequence
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (comp->fe());

   bool isAOT = comp->compileRelocatableCode();

   TR::Instruction *cursor = cg->getAppendInstruction();

   cursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::lis, node, trgReg, isAOT ? 0 : value>>16, cursor);

   if (value != 0x0)
      {
      TR_ExternalRelocationTargetKind reloType;
      if (node->getSymbol()->castToResolvedMethodSymbol()->isSpecial())
         reloType = TR_SpecialRamMethodConst;
      else if (node->getSymbol()->castToResolvedMethodSymbol()->isStatic())
         reloType = TR_StaticRamMethodConst;
      else if (node->getSymbol()->castToResolvedMethodSymbol()->isVirtual())
         reloType = TR_VirtualRamMethodConst;
      else
         {
         reloType = TR_NoRelocation;
         TR_ASSERT(0,"JNI relocation not supported.");
         }
      if(isAOT)
         cg->addExternalRelocation(new (cg->trHeapMemory()) TR::BeforeBinaryEncodingExternalRelocation(cursor, (uint8_t *) node->getSymbolReference(),
               node ? (uint8_t *)(intptr_t)node->getInlinedSiteIndex() : (uint8_t *)-1,
                  reloType, cg),
                  __FILE__, __LINE__, node);
      }

   cursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::ori, node, trgReg, trgReg, isAOT ? 0 : value&0x0000ffff, cursor);

   cg->setAppendInstruction(cursor);
   return(cursor);
   }

TR::Instruction *loadAddressRAM(TR::CodeGenerator *cg, TR::Node * node, intptr_t value, TR::Register *trgReg)
   {
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (comp->fe());
   bool isAOT = comp->compileRelocatableCode();
   if (comp->target().is32Bit())
      {
      return loadAddressRAM32(cg, node, (int32_t)value, trgReg);
      }

   // load a 64-bit constant into a register with a fixed 5 instruction sequence
   TR::Instruction *cursor = cg->getAppendInstruction();

   // lis trgReg, upper 16-bits
   cursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::lis, node, trgReg, isAOT? 0: (value>>48) , cursor);
   if (value != 0x0)
      {
      TR_ExternalRelocationTargetKind reloType;
      if (node->getSymbol()->castToResolvedMethodSymbol()->isSpecial())
         reloType = TR_SpecialRamMethodConst;
      else if (node->getSymbol()->castToResolvedMethodSymbol()->isStatic())
         reloType = TR_StaticRamMethodConst;
      else if (node->getSymbol()->castToResolvedMethodSymbol()->isVirtual())
         reloType = TR_VirtualRamMethodConst;
      else
         {
         reloType = TR_NoRelocation;
         TR_ASSERT(0,"JNI relocation not supported.");
         }
      if(isAOT)
         cg->addExternalRelocation(new (cg->trHeapMemory()) TR::BeforeBinaryEncodingExternalRelocation(cursor, (uint8_t *) node->getSymbolReference(),
               node ? (uint8_t *)(intptr_t)node->getInlinedSiteIndex() : (uint8_t *)-1,
                  reloType, cg),
                  __FILE__,__LINE__, node);
      }
   // ori trgReg, trgReg, next 16-bits
   cursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::ori, node, trgReg, trgReg, isAOT ? 0 : ((value>>32) & 0x0000ffff), cursor);
   // shiftli trgReg, trgReg, 32
   cursor = generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldicr, node, trgReg, trgReg, 32, CONSTANT64(0xFFFFFFFF00000000), cursor);
   // oris trgReg, trgReg, next 16-bits
   cursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::oris, node, trgReg, trgReg, isAOT ? 0 : ((value>>16) & 0x0000ffff), cursor);
   // ori trgReg, trgReg, last 16-bits
   cursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::ori, node, trgReg, trgReg, isAOT ? 0 : (value & 0x0000ffff), cursor);

   cg->setAppendInstruction(cursor);

   return(cursor);
   }

TR::Instruction *loadAddressJNI32(TR::CodeGenerator *cg, TR::Node * node, int32_t value, TR::Register *trgReg)
   {
   // load a 32-bit constant into a register with a fixed 2 instruction sequence
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (comp->fe());

   bool isAOT = comp->compileRelocatableCode();

   TR::Instruction *cursor = cg->getAppendInstruction();

   cursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::lis, node, trgReg, isAOT ? 0 : value>>16, cursor);

   if (isAOT && value != 0x0)
      {
      TR_ExternalRelocationTargetKind reloType;
      if (node->getSymbol()->castToResolvedMethodSymbol()->isSpecial())
         reloType = TR_JNISpecialTargetAddress;
      else if (node->getSymbol()->castToResolvedMethodSymbol()->isStatic())
         reloType = TR_JNIStaticTargetAddress;
      else if (node->getSymbol()->castToResolvedMethodSymbol()->isVirtual())
         reloType = TR_JNIVirtualTargetAddress;
      else
         {
         reloType = TR_NoRelocation;
         TR_ASSERT(0,"JNI relocation not supported.");
         }

      TR_RelocationRecordInformation *info = new (comp->trHeapMemory()) TR_RelocationRecordInformation();
      info->data1 = 0;
      info->data2 = reinterpret_cast<uintptr_t>(node->getSymbolReference());
      int16_t inlinedSiteIndex = node ? node->getInlinedSiteIndex() : -1;
      info->data3 = static_cast<uintptr_t>(inlinedSiteIndex);

      cg->addExternalRelocation(
         new (cg->trHeapMemory()) TR::BeforeBinaryEncodingExternalRelocation(
            cursor,
            reinterpret_cast<uint8_t *>(info),
            reloType,
            cg),
         __FILE__, __LINE__, node);
      }

   cursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::ori, node, trgReg, trgReg, isAOT ? 0 : value&0x0000ffff, cursor);

   cg->setAppendInstruction(cursor);
   return(cursor);
   }

TR::Instruction *loadAddressJNI(TR::CodeGenerator *cg, TR::Node * node, intptr_t value, TR::Register *trgReg)
   {
   TR::Compilation *comp = cg->comp();
   TR_J9VMBase *fej9 = (TR_J9VMBase *) (comp->fe());
   bool isAOT = comp->compileRelocatableCode();
   if (comp->target().is32Bit())
      {
      return loadAddressJNI32(cg, node, (int32_t)value, trgReg);
      }

   // load a 64-bit constant into a register with a fixed 5 instruction sequence
   TR::Instruction *cursor = cg->getAppendInstruction();

   // lis trgReg, upper 16-bits
   cursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::lis, node, trgReg, isAOT? 0: (value>>48) , cursor);
   if (isAOT && value != 0x0)
      {
      TR_ExternalRelocationTargetKind reloType;
      if (node->getSymbol()->castToResolvedMethodSymbol()->isSpecial())
         reloType = TR_JNISpecialTargetAddress;
      else if (node->getSymbol()->castToResolvedMethodSymbol()->isStatic())
         reloType = TR_JNIStaticTargetAddress;
      else if (node->getSymbol()->castToResolvedMethodSymbol()->isVirtual())
         reloType = TR_JNIVirtualTargetAddress;
      else
         {
         reloType = TR_NoRelocation;
         TR_ASSERT(0,"JNI relocation not supported.");
         }

      TR_RelocationRecordInformation *info = new (comp->trHeapMemory()) TR_RelocationRecordInformation();
      info->data1 = 0;
      info->data2 = reinterpret_cast<uintptr_t>(node->getSymbolReference());
      int16_t inlinedSiteIndex = node ? node->getInlinedSiteIndex() : -1;
      info->data3 = static_cast<uintptr_t>(inlinedSiteIndex);

      cg->addExternalRelocation(
         new (cg->trHeapMemory()) TR::BeforeBinaryEncodingExternalRelocation(
            cursor,
            reinterpret_cast<uint8_t *>(info),
            reloType,
            cg),
         __FILE__, __LINE__, node);
      }
   // ori trgReg, trgReg, next 16-bits
   cursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::ori, node, trgReg, trgReg, isAOT ? 0 : ((value>>32) & 0x0000ffff), cursor);
   // shiftli trgReg, trgReg, 32
   cursor = generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldicr, node, trgReg, trgReg, 32, CONSTANT64(0xFFFFFFFF00000000), cursor);
   // oris trgReg, trgReg, next 16-bits
   cursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::oris, node, trgReg, trgReg, isAOT ? 0 : ((value>>16) & 0x0000ffff), cursor);
   // ori trgReg, trgReg, last 16-bits
   cursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::ori, node, trgReg, trgReg, isAOT ? 0 : (value & 0x0000ffff), cursor);

   cg->setAppendInstruction(cursor);

   return(cursor);
   }

TR::Register *J9::Power::TreeEvaluator::directCallEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR::SymbolReference *symRef    = node->getSymbolReference();
   TR::MethodSymbol    *callee    = symRef->getSymbol()->castToMethodSymbol();
   TR::Linkage      *linkage;
   TR::Register        *returnRegister;

   if (callee->getRecognizedMethod() >= TR::FirstVectorMethod &&
       callee->getRecognizedMethod() <= TR::LastVectorMethod &&
       node->getOpCode().isVectorOpCode()) // was vectorized
      {
      TR_ASSERT_FATAL_WITH_NODE(node, false, "vcall is not supported for this Vector API method\n");
      }

   if (!cg->inlineDirectCall(node, returnRegister))
      {
      TR::SymbolReferenceTable *symRefTab = cg->comp()->getSymRefTab();

      // Non-helpers supported by code gen. are expected to be inlined
      if (symRefTab->isNonHelper(symRef))
         {
         TR_ASSERT(!cg->supportsNonHelper(symRefTab->getNonHelperSymbol(symRef)),
                   "Non-helper %d was not inlined, but was expected to be.\n",
                   symRefTab->getNonHelperSymbol(symRef));
         }

      linkage = cg->deriveCallingLinkage(node, false);
      returnRegister = linkage->buildDirectDispatch(node);
      }

   return returnRegister;
   }


TR::Register *J9::Power::TreeEvaluator::setmemoryEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   TR::Compilation *comp = cg->comp();
   TR::Node             *dstBaseAddrNode, *dstOffsetNode, *dstAddrNode, *lengthNode, *valueNode;

   bool arrayCheckNeeded;

   // IL tree structure depends on whether or not it's been determined that a runtime arrayCHK is needed:
   // if node has four children (i.e.: object base address and offset are separate), need array check
   // if node three children (i.e.: object base address and offset have already been added together), don't need array check
   if (node->getNumChildren() == 4)
      {
      arrayCheckNeeded = true;

      dstBaseAddrNode = node->getChild(0);
      dstOffsetNode = node->getChild(1);
      dstAddrNode = NULL;
      lengthNode = node->getChild(2);
      valueNode = node->getChild(3);
      }
   else //i.e.: node->getNumChildren() == 3
      {
      arrayCheckNeeded = false;

      dstBaseAddrNode = NULL;
      dstOffsetNode = NULL;
      dstAddrNode = node->getChild(0);
      lengthNode = node->getChild(1);
      valueNode = node->getChild(2);
      }

   TR::Register         *dstBaseAddrReg, *dstOffsetReg, *dstAddrReg, *lengthReg, *valueReg;

   // if the offset is a constant value less than 16 bits, then we dont need a separate register for it
   bool useOffsetAsImmVal = dstOffsetNode && dstOffsetNode->getOpCode().isLoadConst() &&
                            (dstOffsetNode->getConstValue() >= LOWER_IMMED) && (dstOffsetNode->getConstValue() <= UPPER_IMMED);

   bool stopUsingCopyRegBase = dstBaseAddrNode ? TR::TreeEvaluator::stopUsingCopyReg(dstBaseAddrNode, dstBaseAddrReg, cg) : false;
   bool stopUsingCopyRegAddr = dstAddrNode ? TR::TreeEvaluator::stopUsingCopyReg(dstAddrNode, dstAddrReg, cg) : false ;

   bool stopUsingCopyRegOffset, stopUsingCopyRegLen, stopUsingCopyRegVal;

   //dstOffsetNode (type: long)
   if (dstOffsetNode && !useOffsetAsImmVal) //only want to allocate a register for dstoffset if we're using it for the array check AND it isn't a constant
      {
      if (!cg->canClobberNodesRegister(lengthNode)) //only need to copy dstOffset into another register if the current one isn't clobberable
         {
         if (cg->comp()->target().is32Bit()) //on 32-bit systems, need to grab the lower 32 bits of offset from the register pair
            {
            dstOffsetReg = cg->evaluate(dstOffsetNode);
            TR::Register *offsetCopyReg = cg->allocateRegister();
            generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, dstOffsetNode, offsetCopyReg, dstOffsetReg->getLowOrder());

            dstOffsetReg = offsetCopyReg;
            stopUsingCopyRegOffset = true;
            }
         else
            {
            stopUsingCopyRegOffset = TR::TreeEvaluator::stopUsingCopyReg(dstOffsetNode, dstOffsetReg, cg);
            }
         }
      else
         {
         dstOffsetReg = cg->evaluate(dstOffsetNode);

         if (cg->comp()->target().is32Bit()) //on 32-bit systems, need to grab the lower 32 bits of offset from the register pair
            dstOffsetReg = dstOffsetReg->getLowOrder();

         stopUsingCopyRegOffset = false;
         }
      }
   else
      {
      stopUsingCopyRegOffset = false;
      }

   //lengthNode (type: long)
   lengthReg = cg->evaluate(lengthNode);
   if (!cg->canClobberNodesRegister(lengthNode))
      {
      TR::Register *lenCopyReg = cg->allocateRegister();

      if (cg->comp()->target().is32Bit()) //on 32-bit systems, need to grab the lower 32 bits of length from the register pair
         generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, lengthNode, lenCopyReg, lengthReg->getLowOrder());
      else //on 64-bit system, can just do a normal copy
         generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, lengthNode, lenCopyReg, lengthReg);

      lengthReg = lenCopyReg;
      stopUsingCopyRegLen = true;
      }
   else
      {
      if (cg->comp()->target().is32Bit()) //on 32-bit system, need to grab lower 32 bits of length from the register pair
         lengthReg = lengthReg->getLowOrder();

      stopUsingCopyRegLen = false;
      }

   //valueNode (type: byte)
   valueReg = cg->evaluate(valueNode);
   if (cg->comp()->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P8))
      {
      //on P8 or higher, we can use vector instructions to cut down on loop iterations and residual tests -> need to copy valueReg into a VSX register
      TR::Register *valVectorReg = cg->allocateRegister(TR_VRF);
      generateTrg1Src1Instruction(cg, TR::InstOpCode::mtvsrd, valueNode, valVectorReg, valueReg);

      valueReg = valVectorReg;
      stopUsingCopyRegVal = true;
      }
   else if (!cg->canClobberNodesRegister(valueNode))
      {
      TR::Register *valCopyReg = cg->allocateRegister();
      generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, valueNode, valCopyReg, valueReg);

      valueReg = valCopyReg;
      stopUsingCopyRegVal = true;
      }

   TR::LabelSymbol * residualLabel =  generateLabelSymbol(cg);
   TR::LabelSymbol * loopStartLabel =  generateLabelSymbol(cg);
   TR::LabelSymbol * doneLabel =  generateLabelSymbol(cg);

   //these labels are not needed for the vector approach to storing to residual bytes (i.e.: P10+)
   TR::LabelSymbol *label8aligned, *label4aligned, *label2aligned, *label1aligned;

   if (!cg->comp()->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P10))
      {
      label8aligned =  generateLabelSymbol(cg);
      label4aligned =  generateLabelSymbol(cg);
      label2aligned =  generateLabelSymbol(cg);
      label1aligned =  generateLabelSymbol(cg);
      }

   TR::RegisterDependencyConditions *conditions;
   int32_t numDeps = 6;

   //need extra register for offset only if it isn't already included in the destination address AND it isn't a constant
   if (arrayCheckNeeded && !useOffsetAsImmVal)
      numDeps++;

   conditions = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(numDeps, numDeps, cg->trMemory());
   TR::Register *cndReg = cg->allocateRegister(TR_CCR);
   TR::addDependency(conditions, cndReg, TR::RealRegister::cr0, TR_CCR, cg);

   if (arrayCheckNeeded)
      {
      //dstBaseAddrReg holds the address of the object being written to, so need to exclude GPR0
      TR::addDependency(conditions, dstBaseAddrReg, TR::RealRegister::NoReg, TR_GPR, cg);
      conditions->getPostConditions()->getRegisterDependency(conditions->getAddCursorForPost() - 1)->setExcludeGPR0();

      if (!useOffsetAsImmVal)
         TR::addDependency(conditions, dstOffsetReg, TR::RealRegister::NoReg, TR_GPR, cg);
      }
   else
      {
      //dstAddrReg holds the address of the object being written to, so need to exclude GPR0
      TR::addDependency(conditions, dstAddrReg, TR::RealRegister::NoReg, TR_GPR, cg);
      conditions->getPostConditions()->getRegisterDependency(1)->setExcludeGPR0();
      }

   TR::addDependency(conditions, lengthReg, TR::RealRegister::NoReg, TR_GPR, cg);
   TR::addDependency(conditions, valueReg, TR::RealRegister::NoReg, TR_GPR, cg);

   //temp1Reg will later be used to hold the J9Class flags for the object at dst, so need to exclude GPR0
   TR::Register * temp1Reg = cg->allocateRegister();
   TR::addDependency(conditions, temp1Reg, TR::RealRegister::NoReg, TR_GPR, cg);
   conditions->getPostConditions()->getRegisterDependency(conditions->getAddCursorForPost() - 1)->setExcludeGPR0();

   TR::Register * temp2Reg = cg->allocateRegister();
   TR::addDependency(conditions, temp2Reg, TR::RealRegister::NoReg, TR_GPR, cg);


#if defined (J9VM_GC_SPARSE_HEAP_ALLOCATION)

   if (arrayCheckNeeded) // CASE (3)
      {
      // There are two scenarios in which we DON'T want to modify the dest base address:
      // 1.) If the object is NULL (since we can't load dataAddr from a NULL pointer)
      // 2.) If the object is a non-array object
      // So two checks are required (NULL, Array) to determine whether dataAddr should be loaded or not
      TR::LabelSymbol *noDataAddr = generateLabelSymbol(cg);

      // We only want to generate a runtime NULL check if the status of the object (i.e.: whether it is NULL or non-NULL)
      // is NOT known. Note that if the object is known to be NULL, arrayCheckNeeded will be false, so there is no need to check
      // that condition here.
      if (!dstBaseAddrNode->isNonNull())
         {
         //generate NULL test
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::Op_cmpi, node, cndReg, dstBaseAddrReg, 0);
         generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, noDataAddr, cndReg);
         }

      //Array Check
      TR::Register *dstClassInfoReg = temp1Reg;
      TR::Register *arrayFlagReg = temp2Reg;

      //load dst class info into temp1Reg
      if (TR::Compiler->om.compressObjectReferences())
         generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, dstClassInfoReg,
            TR::MemoryReference::createWithDisplacement(cg, dstBaseAddrReg, static_cast<int32_t>(TR::Compiler->om.offsetOfObjectVftField()), 4));
      else
         generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, dstClassInfoReg,
            TR::MemoryReference::createWithDisplacement(cg, dstBaseAddrReg, static_cast<int32_t>(TR::Compiler->om.offsetOfObjectVftField()), TR::Compiler->om.sizeofReferenceAddress()));

      TR::TreeEvaluator::generateVFTMaskInstruction(cg, node, dstClassInfoReg);

      TR::MemoryReference *dstClassMR = TR::MemoryReference::createWithDisplacement(cg, dstClassInfoReg, offsetof(J9Class, classDepthAndFlags), TR::Compiler->om.sizeofReferenceAddress());
      generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, dstClassInfoReg, dstClassMR);

      //generate array check
      int32_t arrayFlagValue = comp->fej9()->getFlagValueForArrayCheck();
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andis_r, node, arrayFlagReg, dstClassInfoReg, arrayFlagValue >> 16);

      //if object is not an array (i.e.: temp1Reg & temp2Reg == 0), skip adjusting dstBaseAddr and dstOffset
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, noDataAddr, cndReg);

      //load dataAddr if object is array:
      TR::MemoryReference *dataAddrSlotMR = TR::MemoryReference::createWithDisplacement(cg, dstBaseAddrReg, comp->fej9()->getOffsetOfContiguousDataAddrField(), TR::Compiler->om.sizeofReferenceAddress());
      generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, dstBaseAddrReg, dataAddrSlotMR);

      //arrayCHK will skip to here if object is not an array
      generateLabelInstruction(cg, TR::InstOpCode::label, node, noDataAddr);

      //calculate dstAddr = dstBaseAddr + dstOffset
      dstAddrReg = dstBaseAddrReg;

      if (useOffsetAsImmVal)
         {
         int offsetImmVal = dstOffsetNode->getConstValue();
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, dstAddrReg, dstBaseAddrReg, offsetImmVal);
         }
      else
         generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, dstAddrReg, dstBaseAddrReg, dstOffsetReg);
      }

#endif /* J9VM_GC_SPARSE_HEAP_ALLOCATION */

   // assemble the double word value from byte value
   if (cg->comp()->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P8))
      {
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::vspltb, valueNode, valueReg, valueReg, 7);
      }
   else
      {
      generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldimi, node, valueReg, valueReg,  8,  CONSTANT64(0x000000000000FF00));
      generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldimi, node, valueReg, valueReg,  16, CONSTANT64(0x00000000FFFF0000));
      generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldimi, node, valueReg, valueReg,  32, CONSTANT64(0xFFFFFFFF00000000));
      }

   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::Op_cmpli, node, cndReg, lengthReg, 32);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::blt, node, residualLabel, cndReg);

   generateTrg1Src1ImmInstruction(cg, lengthNode->getType().isInt32() ? TR::InstOpCode::srawi : TR::InstOpCode::sradi, node, temp1Reg, lengthReg, 5);
   generateSrc1Instruction(cg, TR::InstOpCode::mtctr, node, temp1Reg);
   generateLabelInstruction(cg, TR::InstOpCode::label, node, loopStartLabel);

   //store designated value to memory in chunks of 32 bytes
   if (cg->comp()->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P8))
      {
      //on P8 and higher, we can use vector instructions to cut down on loop iterations/number of stores
      generateMemSrc1Instruction(cg, TR::InstOpCode::stxvd2x, node, TR::MemoryReference::createWithIndexReg(cg, NULL, dstAddrReg, 16), valueReg);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, dstAddrReg, dstAddrReg, 16);
      generateMemSrc1Instruction(cg, TR::InstOpCode::stxvd2x, node, TR::MemoryReference::createWithIndexReg(cg, NULL, dstAddrReg, 16), valueReg);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, dstAddrReg, dstAddrReg, 16);
      }
   else
      {
      generateMemSrc1Instruction(cg, TR::InstOpCode::std, node, TR::MemoryReference::createWithDisplacement(cg, dstAddrReg, 0, 8), valueReg);
      generateMemSrc1Instruction(cg, TR::InstOpCode::std, node, TR::MemoryReference::createWithDisplacement(cg, dstAddrReg, 8, 8), valueReg);
      generateMemSrc1Instruction(cg, TR::InstOpCode::std, node, TR::MemoryReference::createWithDisplacement(cg, dstAddrReg, 16, 8), valueReg);
      generateMemSrc1Instruction(cg, TR::InstOpCode::std, node, TR::MemoryReference::createWithDisplacement(cg, dstAddrReg, 24, 8), valueReg);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, dstAddrReg, dstAddrReg, 32);
      }

   //decrement counter and return to start of loop
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bdnz, node, loopStartLabel, cndReg);

   //loop exit
   generateLabelInstruction(cg, TR::InstOpCode::label, node, residualLabel);

   //Set residual bytes (max number of residual bytes = 31 = 0x1F)
   if (cg->comp()->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P10)) //on P10, we can use stxvl to store all residual bytes efficiently
      {
      //First 16 byte segment
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, temp1Reg, lengthReg, 16); //get first hex char (can only be 0 or 1)
      generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, temp2Reg, temp1Reg); //keep a copy of first hex char

      //store to memory
      //NOTE: due to a quirk of the stxvl instruction on P10, the number of residual bytes must be shifted over before it can be used
      generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldicr, node, temp1Reg, temp1Reg, 56, CONSTANT64(0xFF00000000000000));
      generateSrc3Instruction(cg, TR::InstOpCode::stxvl, node, valueReg, dstAddrReg, temp1Reg);

      //advance to next 16 byte chunk IF number of residual bytes >= 16
      generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, dstAddrReg, dstAddrReg, temp2Reg);

      //Second 16 byte segment
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, temp1Reg, lengthReg, 15); //get second hex char
      generateTrg1Src1Imm2Instruction(cg, TR::InstOpCode::rldicr, node, temp1Reg, temp1Reg, 56, CONSTANT64(0xFF00000000000000)); //shift num residual bytes
      generateSrc3Instruction(cg, TR::InstOpCode::stxvl, node, valueReg, dstAddrReg, temp1Reg); //store to memory
      }
   else
      {
      TR::Register *valueResidueReg;

      if (cg->comp()->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P8))
         {
         //since P8 and P9 used the vector approach, we first need to copy valueReg back into a GPR
         generateTrg1Src1Instruction(cg, TR::InstOpCode::mfvsrd, node, temp2Reg, valueReg);
         valueResidueReg = temp2Reg;
         }
      else
         valueResidueReg = valueReg;

      //check if residual < 16
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, temp1Reg, lengthReg, 16);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, label8aligned, cndReg);
      generateMemSrc1Instruction(cg, TR::InstOpCode::std, node, TR::MemoryReference::createWithDisplacement(cg, dstAddrReg, 0, 8), valueResidueReg);
      generateMemSrc1Instruction(cg, TR::InstOpCode::std, node, TR::MemoryReference::createWithDisplacement(cg, dstAddrReg, 8, 8), valueResidueReg);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, dstAddrReg, dstAddrReg, 16);

      generateLabelInstruction(cg, TR::InstOpCode::label, node, label8aligned); //check if residual < 8
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, temp1Reg, lengthReg, 8);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, label4aligned, cndReg);
      generateMemSrc1Instruction(cg, TR::InstOpCode::std, node, TR::MemoryReference::createWithDisplacement(cg, dstAddrReg, 0, 8), valueResidueReg);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, dstAddrReg, dstAddrReg, 8);

      generateLabelInstruction(cg, TR::InstOpCode::label, node, label4aligned); //check if residual < 4
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, temp1Reg, lengthReg, 4);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, label2aligned, cndReg);
      generateMemSrc1Instruction(cg, TR::InstOpCode::stw, node, TR::MemoryReference::createWithDisplacement(cg, dstAddrReg, 0, 4), valueResidueReg);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, dstAddrReg, dstAddrReg, 4);

      generateLabelInstruction(cg, TR::InstOpCode::label, node, label2aligned); //check if residual < 2
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, temp1Reg, lengthReg, 2);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, label1aligned, cndReg);
      generateMemSrc1Instruction(cg, TR::InstOpCode::sth, node, TR::MemoryReference::createWithDisplacement(cg, dstAddrReg, 0, 2), valueResidueReg);
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::addi, node, dstAddrReg, dstAddrReg, 2);

      generateLabelInstruction(cg, TR::InstOpCode::label, node, label1aligned); //residual <= 1
      generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::andi_r, node, temp1Reg, lengthReg, 1);
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, doneLabel, cndReg);
      generateMemSrc1Instruction(cg, TR::InstOpCode::stb, node, TR::MemoryReference::createWithDisplacement(cg, dstAddrReg, 0, 1), valueResidueReg);
      }

   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel, conditions);

   if (stopUsingCopyRegBase)
      cg->stopUsingRegister(dstBaseAddrReg);
   if (stopUsingCopyRegOffset)
      cg->stopUsingRegister(dstOffsetReg);
   if (stopUsingCopyRegAddr)
      cg->stopUsingRegister(dstAddrReg);
   if (stopUsingCopyRegLen)
      cg->stopUsingRegister(lengthReg);
   if (stopUsingCopyRegVal)
      cg->stopUsingRegister(valueReg);

   cg->stopUsingRegister(cndReg);
   cg->stopUsingRegister(temp1Reg);
   cg->stopUsingRegister(temp2Reg);

   if (dstBaseAddrNode) cg->decReferenceCount(dstBaseAddrNode);
   if (dstOffsetNode) cg->decReferenceCount(dstOffsetNode);
   if (dstAddrNode) cg->decReferenceCount(dstAddrNode);
   cg->decReferenceCount(lengthNode);
   cg->decReferenceCount(valueNode);

   return(NULL);
   }


TR::Register *J9::Power::TreeEvaluator::tstartEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   // tstart
   //  - persistentFailNode
   //  - transientFailNode
   //  - fallThroughNode (next block)
   //  - monitorObject
   //fprintf(stderr,"tstart Start\n");
   TR::Node *persistentFailureNode = node->getFirstChild();
   TR::Node *transientFailureNode  = node->getSecondChild();
   TR::Node *fallThrough = node->getThirdChild();
   TR::Node *objNode = node->getChild(3);
   TR::Node *GRANode = NULL;
   TR::Compilation *comp = cg->comp();

   TR::LabelSymbol *labelPersistentFailure = persistentFailureNode->getBranchDestination()->getNode()->getLabel();
   TR::LabelSymbol *labelTransientFailure  = transientFailureNode->getBranchDestination()->getNode()->getLabel();
   TR::LabelSymbol *labelfallThrough  = fallThrough->getBranchDestination()->getNode()->getLabel();
   TR::LabelSymbol *startLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *lockwordLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *noTexStatsLabel = generateLabelSymbol(cg);

   TR::Register *objReg = cg->evaluate(objNode);
   TR::Register *monReg = cg->allocateRegister();
   TR::Register *cndReg = cg->allocateRegister(TR_CCR);
   TR::Register *tempReg = cg->allocateRegister();
   //TR::Register *temp2Reg = cg->allocateRegister();
   //TR::Register *temp3Reg = cg->allocateRegister();

   // Dependency conditions for this evaluator's internal control flow
   TR::RegisterDependencyConditions *conditions = NULL;
   // Dependency conditions for GRA
   TR::RegisterDependencyConditions *fallThroughConditions = NULL;
   TR::RegisterDependencyConditions *persistentConditions = NULL;
   TR::RegisterDependencyConditions *transientConditions  = NULL;

   conditions = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(4, 4, cg->trMemory());

   if (fallThrough->getNumChildren() != 0)
      {
      GRANode = fallThrough->getFirstChild();
      cg->evaluate(GRANode);
      fallThroughConditions = generateRegisterDependencyConditions(cg, GRANode, 0);
      cg->decReferenceCount(GRANode);
      }

   if (persistentFailureNode->getNumChildren() != 0)
      {
      GRANode = persistentFailureNode->getFirstChild();
      cg->evaluate(GRANode);
      persistentConditions = generateRegisterDependencyConditions(cg, GRANode, 0);
      cg->decReferenceCount(GRANode);
      }

   if (transientFailureNode->getNumChildren() != 0)
      {
      GRANode = transientFailureNode->getFirstChild();
      cg->evaluate(GRANode);
      transientConditions = generateRegisterDependencyConditions(cg, GRANode, 0);
      cg->decReferenceCount(GRANode);
      }

   uint32_t conditionCursor = conditions->getAddCursorForPre();
   TR::addDependency(conditions, objReg, TR::RealRegister::NoReg, TR_GPR, cg);
   conditions->getPreConditions()->getRegisterDependency(conditionCursor)->setExcludeGPR0();
   conditions->getPostConditions()->getRegisterDependency(conditionCursor++)->setExcludeGPR0();
   TR::addDependency(conditions, cndReg, TR::RealRegister::cr0, TR_CCR, cg);
   TR::addDependency(conditions, monReg, TR::RealRegister::NoReg, TR_GPR, cg);
   TR::addDependency(conditions, tempReg, TR::RealRegister::NoReg, TR_GPR, cg);

   static char * debugTMTLE = feGetEnv("debugTMTLE");

   if (debugTMTLE)
      printf ("\nTM: use TM TLE in %s (%s) %p", comp->signature(), comp->getHotnessName(comp->getMethodHotness()), node);

   if (debugTMTLE )
      {
      if (persistentConditions)
        {
        generateDepLabelInstruction(cg, TR::InstOpCode::b, node, labelPersistentFailure, persistentConditions);
        }
     else
        {
        generateLabelInstruction(cg, TR::InstOpCode::b, node, labelPersistentFailure);
         }
      }
   else
      {
      generateInstruction(cg, TR::InstOpCode::tbegin_r, node);
      }

      generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node,
                                           lockwordLabel, cndReg);

   generateTrg1Instruction(cg, TR::InstOpCode::mftexasru, node, monReg);

   // This mask *should* correspond to the TEXASR failure persistent bit (8)
   // NOT the abort bit (31, indicates an explicit abort)
   //loadConstant(cg, node, 0x01000001, tempReg);
   loadConstant(cg, node, 0x01000000, tempReg);
   generateTrg1Src2Instruction(cg, TR::InstOpCode::and_r, node, tempReg, tempReg, monReg);
   if (transientConditions)
      {
      generateDepConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, labelTransientFailure, cndReg, transientConditions);
      }
   else
      {
      generateConditionalBranchInstruction(cg, TR::InstOpCode::beq, node, labelTransientFailure, cndReg);
      }
   if (persistentConditions)
      {
      generateDepLabelInstruction(cg, TR::InstOpCode::b, node, labelPersistentFailure, persistentConditions);
      }
   else
      {
      generateLabelInstruction(cg, TR::InstOpCode::b, node, labelPersistentFailure);
      }

   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, lockwordLabel, conditions);

   int32_t lwOffset = cg->fej9()->getByteOffsetToLockword((TR_OpaqueClassBlock *) cg->getMonClass(node));

   if (comp->target().is64Bit() && cg->fej9()->generateCompressedLockWord())
      {
      generateTrg1MemInstruction(cg, TR::InstOpCode::lwz, node, monReg,
                                 TR::MemoryReference::createWithDisplacement(cg, objReg, lwOffset, 4));
      }
   else
      {
      generateTrg1MemInstruction(cg,TR::InstOpCode::Op_load, node, monReg,
                                 TR::MemoryReference::createWithDisplacement(cg, objReg, lwOffset, TR::Compiler->om.sizeofReferenceAddress()));
      }

   // abort if lock is held
   if (comp->target().is32Bit() || cg->fej9()->generateCompressedLockWord())
      {
      generateSrc1Instruction(cg, TR::InstOpCode::tabortwneqi_r, node, monReg, 0);
      }
   else
      {
      generateSrc1Instruction(cg, TR::InstOpCode::tabortdneqi_r, node, monReg, 0);
      }

   TR::TreeTop *BBendTreeTop = cg->getCurrentEvaluationTreeTop()->getNextTreeTop();
   TR::TreeTop *BBstartTreeTop = NULL;
   if (BBendTreeTop)
      BBstartTreeTop = BBendTreeTop->getNextTreeTop();
   TR::TreeTop *fallThruTarget = fallThrough->getBranchDestination();

   if (BBstartTreeTop && (fallThruTarget == BBstartTreeTop))
      {
      if (fallThroughConditions)
         generateDepLabelInstruction(cg, TR::InstOpCode::label, node, startLabel, fallThroughConditions);
      else
         generateLabelInstruction(cg, TR::InstOpCode::label, node, startLabel);
      }
   else
      {
      if (fallThroughConditions)
         generateDepLabelInstruction(cg, TR::InstOpCode::b, node, labelfallThrough, fallThroughConditions);
      else
         generateLabelInstruction(cg, TR::InstOpCode::b, node, labelfallThrough);
      }

   cg->stopUsingRegister(monReg);
   cg->stopUsingRegister(cndReg);
   cg->stopUsingRegister(tempReg);

   cg->decReferenceCount(objNode);
   cg->decReferenceCount(persistentFailureNode);
   cg->decReferenceCount(transientFailureNode);
   cg->decReferenceCount(fallThrough);

   return NULL;
   }

TR::Register *J9::Power::TreeEvaluator::tfinishEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   generateInstruction(cg, TR::InstOpCode::tend_r, node);
   return NULL;
   }

TR::Register *J9::Power::TreeEvaluator::tabortEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   generateInstruction(cg, TR::InstOpCode::tabort_r, node);
   return NULL;
   }

TR::Register *J9::Power::TreeEvaluator::arraycopyEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
#ifdef OMR_GC_CONCURRENT_SCAVENGER
   /*
    * This version of arraycopyEvaluator is designed to handle the special case where read barriers are
    * needed for field loads. At the time of writing, read barriers are used for Concurrent Scavenge GC.
    * If there are no read barriers then the original implementation of arraycopyEvaluator can be used.
    */
   if (TR::Compiler->om.readBarrierType() == gc_modron_readbar_none ||
          !node->chkNoArrayStoreCheckArrayCopy() ||
          !node->isReferenceArrayCopy() ||
          debug("noArrayCopy")
      )
      {
      return OMR::TreeEvaluatorConnector::arraycopyEvaluator(node, cg);
      }

   TR::Compilation *comp = cg->comp();
   TR::Instruction      *gcPoint;
   TR::Node             *srcObjNode, *dstObjNode, *srcAddrNode, *dstAddrNode, *lengthNode;
   TR::Register         *srcObjReg, *dstObjReg, *srcAddrReg, *dstAddrReg, *lengthReg;
   bool stopUsingCopyReg1, stopUsingCopyReg2, stopUsingCopyReg3, stopUsingCopyReg4, stopUsingCopyReg5 = false;

   srcObjNode = node->getChild(0);
   dstObjNode = node->getChild(1);
   srcAddrNode = node->getChild(2);
   dstAddrNode = node->getChild(3);
   lengthNode = node->getChild(4);

   // These calls evaluate the nodes and give back registers that can be clobbered if needed.
   stopUsingCopyReg1 = TR::TreeEvaluator::stopUsingCopyReg(srcObjNode, srcObjReg, cg);
   stopUsingCopyReg2 = TR::TreeEvaluator::stopUsingCopyReg(dstObjNode, dstObjReg, cg);
   stopUsingCopyReg3 = TR::TreeEvaluator::stopUsingCopyReg(srcAddrNode, srcAddrReg, cg);
   stopUsingCopyReg4 = TR::TreeEvaluator::stopUsingCopyReg(dstAddrNode, dstAddrReg, cg);

   lengthReg = cg->evaluate(lengthNode);
   if (!cg->canClobberNodesRegister(lengthNode))
      {
      TR::Register *lenCopyReg = cg->allocateRegister();
      generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, lengthNode, lenCopyReg, lengthReg);
      lengthReg = lenCopyReg;
      stopUsingCopyReg5 = true;
      }

   // Inline forward arrayCopy with constant length.
   int64_t len = -1;
   if (node->isForwardArrayCopy() && lengthNode->getOpCode().isLoadConst())
      {
      len = (lengthNode->getType().isInt32() ? lengthNode->getInt() : lengthNode->getLongInt());

      // inlineArrayCopy_ICF is not currently capable of handling very long lengths correctly. Under some circumstances,
      // it will generate an li instruction with an out-of-bounds immediate, which triggers an assert in the binary
      // encoder.
      if (len >= 0 && len < MAX_PPC_ARRAYCOPY_INLINE)
         {
         /*
          * This path generates code to perform a runtime check on whether concurrent GC is done moving objects or not.
          * If it isn't, a call to referenceArrayCopy helper should be made.
          * If it is, using the inlined array copy code path is okay.
          */
         int32_t groups;

         // Our hands are tied somewhat due to the potential ReferenceArrayCopy call
         // We can really work with less registers in P10 case. For readability and
         // maintainability, we are simplifying the logic but using more registers.

         bool postP10CopyInline = cg->comp()->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P10) &&
                                  cg->comp()->target().cpu.supportsFeature(OMR_FEATURE_PPC_HAS_VSX);

         static bool disableLEArrayCopyInline = (feGetEnv("TR_disableLEArrayCopyInline") != NULL);
         bool supportsLEArrayCopyInline = cg->comp()->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P8) &&
                                          !disableLEArrayCopyInline &&
                                          comp->target().cpu.isLittleEndian() &&
                                          comp->target().cpu.hasFPU() &&
                                          comp->target().is64Bit();

         // There are a minimum of 9 dependencies.
         uint8_t numDeps = 9;

         if (comp->target().is64Bit())
            {
            groups = len >> 5;
            }
         else
            {
            groups = len >> 4;
            }

         TR::Register *condReg = cg->allocateRegister(TR_CCR);

         // These registers are used when taking the inlineArrayCopy_ICF path.
         TR::Register *tmp1Reg = cg->allocateRegister(TR_GPR);
         TR::Register *tmp2Reg = NULL;
         TR::Register *tmp3Reg = NULL;
         TR::Register *tmp4Reg = NULL;
         TR::Register *fp1Reg = NULL;
         TR::Register *fp2Reg = NULL;
         TR::Register *fp3Reg = NULL;
         TR::Register *fp4Reg = NULL;

         // These registers are used when taking the referenceArrayCopy helper call path.
         TR::Register *r3Reg = cg->allocateRegister();
         TR::Register *metaReg = cg->getMethodMetaDataRegister();

         if (postP10CopyInline)
            {
            numDeps += 1;
            fp1Reg = cg->allocateRegister(TR_VSX_VECTOR);
            }
         else
            {
            if (groups != 0)
               {
               numDeps += 3;
               tmp2Reg = cg->allocateRegister(TR_GPR);
               tmp3Reg = cg->allocateRegister(TR_GPR);
               tmp4Reg = cg->allocateRegister(TR_GPR);
               }

            if (supportsLEArrayCopyInline)
               {
               numDeps += 4;
               fp1Reg = cg->allocateRegister(TR_FPR);
               fp2Reg = cg->allocateRegister(TR_FPR);
               fp3Reg = cg->allocateRegister(TR_FPR);
               fp4Reg = cg->allocateRegister(TR_FPR);
               }
            }

         /*
          * r3-r8 are used to pass parameters to the referenceArrayCopy helper.
          * r11 is used for the tmp1Reg since r11 gets killed by the trampoline and values put into tmp1Reg are not needed after the trampoline.
          */
         TR::RegisterDependencyConditions *deps = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, numDeps, cg->trMemory());
         deps->addPostCondition(condReg, TR::RealRegister::cr0);
         deps->addPostCondition(metaReg, TR::RealRegister::NoReg);

         deps->addPostCondition(r3Reg, TR::RealRegister::gr3);
         deps->addPostCondition(srcObjReg, TR::RealRegister::gr4);
         deps->addPostCondition(dstObjReg, TR::RealRegister::gr5);
         deps->addPostCondition(srcAddrReg, TR::RealRegister::gr6);
         deps->addPostCondition(dstAddrReg, TR::RealRegister::gr7);
         deps->addPostCondition(lengthReg, TR::RealRegister::gr8);

         deps->addPostCondition( tmp1Reg, TR::RealRegister::gr11);

         if (postP10CopyInline)
            {
            deps->addPostCondition(fp1Reg, TR::RealRegister::NoReg);
            }
         else
            {
            if (groups != 0)
               {
               deps->addPostCondition(tmp2Reg, TR::RealRegister::NoReg);
               deps->addPostCondition(tmp3Reg, TR::RealRegister::NoReg);
               deps->addPostCondition(tmp4Reg, TR::RealRegister::NoReg);
               }

            if (supportsLEArrayCopyInline)
               {
               deps->addPostCondition(fp1Reg, TR::RealRegister::NoReg);
               deps->addPostCondition(fp2Reg, TR::RealRegister::NoReg);
               deps->addPostCondition(fp3Reg, TR::RealRegister::NoReg);
               deps->addPostCondition(fp4Reg, TR::RealRegister::NoReg);
               }
            }

         TR::LabelSymbol *startLabel = generateLabelSymbol(cg);
         TR::LabelSymbol *helperLabel = generateLabelSymbol(cg);
         TR::LabelSymbol *endLabel = generateLabelSymbol(cg);
         startLabel->setStartInternalControlFlow();
         endLabel->setEndInternalControlFlow();

         generateLabelInstruction(cg, TR::InstOpCode::label, node, startLabel);

         // Runtime check for concurrent scavenger.
         generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, tmp1Reg,
               TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, readBarrierRangeCheckTop), TR::Compiler->om.sizeofReferenceAddress()));
         generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::Op_cmpli, node, condReg, tmp1Reg, 0);
         generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, helperLabel, condReg);

         // Generate assembly for inlined version of array copy.
         inlineArrayCopy_ICF(node, len, srcAddrReg, dstAddrReg, cg, condReg,
                             tmp1Reg, tmp2Reg, tmp3Reg, tmp4Reg,
                             fp1Reg, fp2Reg, fp3Reg, fp4Reg);

         generateLabelInstruction(cg, TR::InstOpCode::b, node, endLabel);

         // Start of referenceArrayCopy helper path.
         generateLabelInstruction(cg, TR::InstOpCode::label, node, helperLabel);

         J9::Power::JNILinkage *jniLinkage = (J9::Power::JNILinkage*) cg->getLinkage(TR_J9JNILinkage);
         const TR::PPCLinkageProperties &pp = jniLinkage->getProperties();

         int32_t elementSize;
         if (comp->useCompressedPointers())
            elementSize = TR::Compiler->om.sizeofReferenceField();
         else
            elementSize = (int32_t) TR::Compiler->om.sizeofReferenceAddress();

         // Sign extend non-64bit Integers on LinuxPPC64 as required by the ABI
         if (comp->target().isLinux() && comp->target().is64Bit())
            {
            generateTrg1Src1Instruction(cg, TR::InstOpCode::extsw, node, lengthReg, lengthReg);
            }

         // The C routine expects length measured by slots.
         generateShiftRightLogicalImmediate(cg, node, lengthReg, lengthReg, trailingZeroes(elementSize));

         generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, r3Reg, metaReg);

         TR::RegisterDependencyConditions *helperDeps = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, 0, cg->trMemory());
         TR::SymbolReference *helperSym = comp->getSymRefTab()->findOrCreateRuntimeHelper(TR_referenceArrayCopy);

         gcPoint = generateDepImmSymInstruction(cg, TR::InstOpCode::bl, node, (uintptr_t)helperSym->getMethodAddress(), helperDeps, helperSym);
         gcPoint->PPCNeedsGCMap(pp.getPreservedRegisterMapForGC());

         generateDepLabelInstruction(cg, TR::InstOpCode::label, node, endLabel, deps);

         TR::TreeEvaluator::genWrtbarForArrayCopy(node, srcObjReg, dstObjReg, cg);

         TR::Register *retRegisters[5];
         int retRegCount = 0;
         cg->decReferenceCount(srcObjNode);
         cg->decReferenceCount(dstObjNode);
         cg->decReferenceCount(srcAddrNode);
         cg->decReferenceCount(dstAddrNode);
         cg->decReferenceCount(lengthNode);

         // Don't kill the registers that should not be clobbered
         if (!stopUsingCopyReg1)
            retRegisters[retRegCount++] = srcObjReg;
         if (!stopUsingCopyReg2)
            retRegisters[retRegCount++] = dstObjReg;
         if (!stopUsingCopyReg3)
            retRegisters[retRegCount++] = srcAddrReg;
         if (!stopUsingCopyReg4)
            retRegisters[retRegCount++] = dstAddrReg;
         if (!stopUsingCopyReg5)
            retRegisters[retRegCount++] = lengthReg;

         deps->stopUsingDepRegs(cg, retRegCount, retRegisters);
         cg->machine()->setLinkRegisterKilled(true);
         cg->setHasCall();

         return NULL;
         }
      }

   /*
    * This path also generates code to perform a runtime check on whether concurrent GC is done moving objects or not.
    * If it isn't done, once again a call to referenceArrayCopy helper should be made.
    * If it is done, using the assembly helpers code path is okay.
    */

   TR::Register *condReg = cg->allocateRegister(TR_CCR);

   // These registers are used when taking the assembly helpers path
   TR::Register *tmp1Reg = cg->allocateRegister(TR_GPR);
   TR::Register *tmp2Reg = cg->allocateRegister(TR_GPR);
   TR::Register *tmp3Reg = cg->allocateRegister(TR_GPR);
   TR::Register *tmp4Reg = cg->allocateRegister(TR_GPR);
   TR::Register *fp1Reg = NULL;
   TR::Register *fp2Reg = NULL;
   TR::Register *vec0Reg = NULL;
   TR::Register *vec1Reg = NULL;

   // These registers are used when taking the referenceArrayCopy helper call path.
   TR::Register *r3Reg = cg->allocateRegister();
   TR::Register *r4Reg = cg->allocateRegister();
   TR::Register *metaReg = cg->getMethodMetaDataRegister();

   // This section calculates the number of dependencies needed by the assembly helpers path.
   bool postP10Copy = cg->comp()->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P10) &&
                      cg->comp()->target().cpu.supportsFeature(OMR_FEATURE_PPC_HAS_VSX);

   static bool disableVSXArrayCopy = (feGetEnv("TR_disableVSXArrayCopy") != NULL);
   bool useVSXForCopy  = cg->comp()->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P8) &&
                         !disableVSXArrayCopy && cg->comp()->target().cpu.supportsFeature(OMR_FEATURE_PPC_HAS_VSX);

   // VSX supercedes FPU.  No reason to offering disable option on this.
   //    POWER8 potentially micro-coded unaligned integer accesses in LE mode,
   //    breaking the guarantee of data atomicity. So, we use floating point
   //    accesses instead.
   bool extraLERequirement  = cg->comp()->target().cpu.isLittleEndian();

#if defined(DEBUG) || defined(PROD_WITH_ASSUMES)
   static bool verboseArrayCopy = (feGetEnv("TR_verboseArrayCopy") != NULL);       //Check which helper is getting used.
   if (verboseArrayCopy)
      fprintf(stderr, "arraycopy [0x%p] isReferenceArrayCopy:[%d] isForwardArrayCopy:[%d] isHalfWordElementArrayCopy:[%d] isWordElementArrayCopy:[%d] %s @ %s\n",
               node,
               0,
               node->isForwardArrayCopy(),
               node->isHalfWordElementArrayCopy(),
               node->isWordElementArrayCopy(),
               comp->signature(),
               comp->getHotnessName(comp->getMethodHotness())
               );
#endif

   /*
    * The minimum number of dependencies used by the assembly helpers path is 8.
    * The number of dependencies added by the referenceArrayCopy helper call path is 5.
    */
   int32_t numDeps = 8 + 5;

   if (postP10Copy)
      {
      // Due to the potential ReferenceArrayCopy call, our hands are tied somewhat.
      // Strictly for arrayCopy call, we don't need that many registers at all.

      numDeps += 4;
      }
   else if (useVSXForCopy)
      {
      vec0Reg = cg->allocateRegister(TR_VRF);
      vec1Reg = cg->allocateRegister(TR_VRF);
      numDeps += 2;
      if (comp->target().is32Bit())
         {
         numDeps += 1;
         }
      if (extraLERequirement)
         {
         fp1Reg = cg->allocateSinglePrecisionRegister();
         fp2Reg = cg->allocateSinglePrecisionRegister();
         numDeps += 4;
         }
      }
   else if (comp->target().is32Bit())
      {
      numDeps += 1;
      if (comp->target().cpu.hasFPU())
         {
         numDeps += 4;
         }
      }
   else if (comp->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P6))
      {
      numDeps += 4;
      }

   TR::RegisterDependencyConditions *deps = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(numDeps, numDeps, cg->trMemory());

   /*
    * Build up the dependency conditions for assembly helper path.
    * Unfortunately, the two different paths have a conflict regarding which real register they want srcAddrReg, dstAddrReg and lengthReg in.
    * Dependencies are set up to favour the fast assembly path. Register moves are used in the slow helper path to move the values to the
    * real registers they are expected to be in.
    */
   TR::addDependency(deps, condReg, TR::RealRegister::cr0, TR_CCR, cg);

   TR::addDependency(deps, lengthReg, TR::RealRegister::gr7, TR_GPR, cg);
   TR::addDependency(deps, srcAddrReg, TR::RealRegister::gr8, TR_GPR, cg);
   TR::addDependency(deps, dstAddrReg, TR::RealRegister::gr9, TR_GPR, cg);

   TR::addDependency(deps, tmp1Reg, TR::RealRegister::gr5, TR_GPR, cg);
   TR::addDependency(deps, tmp2Reg, TR::RealRegister::gr6, TR_GPR, cg);
   TR::addDependency(deps, tmp3Reg, TR::RealRegister::gr0, TR_GPR, cg);
   TR::addDependency(deps, tmp4Reg, TR::RealRegister::gr11, TR_GPR, cg); // Trampoline kills gr11.

   if (postP10Copy)
      {
      TR::addDependency(deps, NULL, TR::RealRegister::vsr8, TR_VSX_VECTOR, cg);
      TR::addDependency(deps, NULL, TR::RealRegister::vsr9, TR_VSX_VECTOR, cg);
      TR::addDependency(deps, NULL, TR::RealRegister::vsr32, TR_VSX_VECTOR, cg);
      TR::addDependency(deps, NULL, TR::RealRegister::vsr33, TR_VSX_VECTOR, cg);
      }
   else if (useVSXForCopy)
      {
      TR::addDependency(deps, vec0Reg, TR::RealRegister::vr0, TR_VRF, cg);
      TR::addDependency(deps, vec1Reg, TR::RealRegister::vr1, TR_VRF, cg);
      if (comp->target().is32Bit())
         {
         TR::addDependency(deps, NULL, TR::RealRegister::gr10, TR_GPR, cg);
         }
      if (extraLERequirement)
         {
         TR::addDependency(deps, fp1Reg, TR::RealRegister::fp8, TR_FPR, cg);
         TR::addDependency(deps, fp2Reg, TR::RealRegister::fp9, TR_FPR, cg);
         TR::addDependency(deps, NULL, TR::RealRegister::fp10, TR_FPR, cg);
         TR::addDependency(deps, NULL, TR::RealRegister::fp11, TR_FPR, cg);
         }
      }
   else if (comp->target().is32Bit())
      {
      TR::addDependency(deps, NULL, TR::RealRegister::gr10, TR_GPR, cg);
      if (comp->target().cpu.hasFPU())
         {
         TR::addDependency(deps, NULL, TR::RealRegister::fp8, TR_FPR, cg);
         TR::addDependency(deps, NULL, TR::RealRegister::fp9, TR_FPR, cg);
         TR::addDependency(deps, NULL, TR::RealRegister::fp10, TR_FPR, cg);
         TR::addDependency(deps, NULL, TR::RealRegister::fp11, TR_FPR, cg);
         }
      }
   else if (comp->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P6))
      {
      // stfdp arrayCopy used
      TR::addDependency(deps, NULL, TR::RealRegister::fp8, TR_FPR, cg);
      TR::addDependency(deps, NULL, TR::RealRegister::fp9, TR_FPR, cg);
      TR::addDependency(deps, NULL, TR::RealRegister::fp10, TR_FPR, cg);
      TR::addDependency(deps, NULL, TR::RealRegister::fp11, TR_FPR, cg);
      }

   // Add dependencies for the referenceArrayCopy helper call path.
   TR::addDependency(deps, r3Reg, TR::RealRegister::gr3, TR_GPR, cg);
   TR::addDependency(deps, r4Reg, TR::RealRegister::gr4, TR_GPR, cg);

   TR::addDependency(deps, srcObjReg, TR::RealRegister::NoReg, TR_GPR, cg);
   TR::addDependency(deps, dstObjReg, TR::RealRegister::NoReg, TR_GPR, cg);
   TR::addDependency(deps, metaReg, TR::RealRegister::NoReg, TR_GPR, cg);

   TR::LabelSymbol *startLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *helperLabel = generateLabelSymbol(cg);
   TR::LabelSymbol *endLabel = generateLabelSymbol(cg);
   startLabel->setStartInternalControlFlow();
   endLabel->setEndInternalControlFlow();

   generateLabelInstruction(cg, TR::InstOpCode::label, node, startLabel);

   // Runtime check for concurrent scavenger.
   generateTrg1MemInstruction(cg, TR::InstOpCode::Op_load, node, tmp1Reg,
         TR::MemoryReference::createWithDisplacement(cg, metaReg, offsetof(J9VMThread, readBarrierRangeCheckTop), TR::Compiler->om.sizeofReferenceAddress()));
   generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::Op_cmpli, node, condReg, tmp1Reg, 0);
   generateConditionalBranchInstruction(cg, TR::InstOpCode::bne, node, helperLabel, condReg);

   // Start of assembly helper path.
   TR_RuntimeHelper helper;

   if (node->isForwardArrayCopy())
      {
      if (postP10Copy)
         {
         helper = TR_PPCpostP10ForwardCopy;
         }
      else if (useVSXForCopy)
         {
         helper = TR_PPCforwardQuadWordArrayCopy_vsx;
         }
      else if (node->isWordElementArrayCopy())
         {
         if (cg->comp()->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P6))
            helper = TR_PPCforwardWordArrayCopy_dp;
         else
            helper = TR_PPCforwardWordArrayCopy;
         }
      else if (node->isHalfWordElementArrayCopy())
         {
         if (cg->comp()->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P6))
            helper = TR_PPCforwardHalfWordArrayCopy_dp;
         else
            helper = TR_PPCforwardHalfWordArrayCopy;
         }
      else
         {
         if (cg->comp()->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P6))
            helper = TR_PPCforwardArrayCopy_dp;
         else
            helper = TR_PPCforwardArrayCopy;
         }
      }
   else // We are not sure it is forward or we have to do backward.
      {
      if (postP10Copy)
         {
         helper = TR_PPCpostP10GenericCopy;
         }
      else if (useVSXForCopy)
         {
         helper = TR_PPCquadWordArrayCopy_vsx;
         }
      else if (node->isWordElementArrayCopy())
         {
         if (cg->comp()->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P6))
            helper = TR_PPCwordArrayCopy_dp;
         else
            helper = TR_PPCwordArrayCopy;
         }
      else if (node->isHalfWordElementArrayCopy())
         {
         if (cg->comp()->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P6))
            helper = TR_PPChalfWordArrayCopy_dp;
         else
            helper = TR_PPChalfWordArrayCopy;
         }
      else
         {
         if (cg->comp()->target().cpu.isAtLeast(OMR_PROCESSOR_PPC_P6))
            helper = TR_PPCarrayCopy_dp;
         else
            helper = TR_PPCarrayCopy;
         }
      }
   TR::TreeEvaluator::generateHelperBranchAndLinkInstruction(helper, node, deps, cg);

   generateLabelInstruction(cg, TR::InstOpCode::b, node, endLabel);

   // Start of referenceArrayCopy helper path.
   generateLabelInstruction(cg, TR::InstOpCode::label, node, helperLabel);

   J9::Power::JNILinkage *jniLinkage = (J9::Power::JNILinkage*) cg->getLinkage(TR_J9JNILinkage);
   const TR::PPCLinkageProperties &pp = jniLinkage->getProperties();

   int32_t elementSize;
   if (comp->useCompressedPointers())
      elementSize = TR::Compiler->om.sizeofReferenceField();
   else
      elementSize = (int32_t) TR::Compiler->om.sizeofReferenceAddress();

   // Sign extend non-64bit Integers on LinuxPPC64 as required by the ABI
   if (comp->target().isLinux() && comp->target().is64Bit())
      {
      generateTrg1Src1Instruction(cg, TR::InstOpCode::extsw, node, lengthReg, lengthReg);
      }

   // The C routine expects length measured by slots.
   generateShiftRightLogicalImmediate(cg, node, lengthReg, lengthReg, trailingZeroes(elementSize));

   /*
    * Parameters are set up here
    * r3 = vmThread
    * r4 = srcObj
    * r5 = dstObj
    * r6 = srcAddr
    * r7 = dstAddr
    * r8 = length
    *
    * CAUTION: Virtual register names are based on their use during the non-helper path so they are misleading after this point.
    * Due to register reuse, pay attention to copying order so that a register is not clobbered too early.
    */
   generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, r3Reg, metaReg);
   generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, r4Reg, srcObjReg);
   generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, tmp1Reg, dstObjReg);    //tmp1Reg is tied to r5.
   generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, tmp2Reg, srcAddrReg);   //tmp2Reg is tied to r6.
   generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, srcAddrReg, lengthReg); //srcAddrReg is tied to r8. Need to copy srcAddrReg first.
   generateTrg1Src1Instruction(cg, TR::InstOpCode::mr, node, lengthReg, dstAddrReg); //lengthReg is tied to r7.  Need to copy lengthReg first.

   TR::RegisterDependencyConditions *helperDeps = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, 0, cg->trMemory());
   TR::SymbolReference *helperSym = comp->getSymRefTab()->findOrCreateRuntimeHelper(TR_referenceArrayCopy);

   gcPoint = generateDepImmSymInstruction(cg, TR::InstOpCode::bl, node, (uintptr_t)helperSym->getMethodAddress(), helperDeps, helperSym);
   gcPoint->PPCNeedsGCMap(pp.getPreservedRegisterMapForGC());

   generateDepLabelInstruction(cg, TR::InstOpCode::label, node, endLabel, deps);

   TR::TreeEvaluator::genWrtbarForArrayCopy(node, srcObjReg, dstObjReg, cg);

   cg->decReferenceCount(srcObjNode);
   cg->decReferenceCount(dstObjNode);
   cg->decReferenceCount(srcAddrNode);
   cg->decReferenceCount(dstAddrNode);
   cg->decReferenceCount(lengthNode);

   TR::Register *retRegisters[5];
   int retRegCount = 0;

   // Don't kill the registers that should not be clobbered
   if (!stopUsingCopyReg1)
      retRegisters[retRegCount++] = srcObjReg;
   if (!stopUsingCopyReg2)
      retRegisters[retRegCount++] = dstObjReg;
   if (!stopUsingCopyReg3)
      retRegisters[retRegCount++] = srcAddrReg;
   if (!stopUsingCopyReg4)
      retRegisters[retRegCount++] = dstAddrReg;
   if (!stopUsingCopyReg5)
      retRegisters[retRegCount++] = lengthReg;

   deps->stopUsingDepRegs(cg, retRegCount, retRegisters);
   cg->machine()->setLinkRegisterKilled(true);
   cg->setHasCall();

   return NULL;
#else /* OMR_GC_CONCURRENT_SCAVENGER */
   return OMR::TreeEvaluatorConnector::arraycopyEvaluator(node, cg);
#endif /* OMR_GC_CONCURRENT_SCAVENGER */
   }

TR::Register *
J9::Power::TreeEvaluator::NULLCHKEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   return TR::TreeEvaluator::evaluateNULLCHKWithPossibleResolve(node, false, cg);
   }

TR::Register *
J9::Power::TreeEvaluator::resolveAndNULLCHKEvaluator(TR::Node *node, TR::CodeGenerator *cg)
   {
   return TR::TreeEvaluator::evaluateNULLCHKWithPossibleResolve(node, true, cg);
   }

TR::Register *
J9::Power::TreeEvaluator::evaluateNULLCHKWithPossibleResolve(TR::Node *node, bool needsResolve, TR::CodeGenerator *cg)
   {
   // NOTE:
   // If in the future no code is generated for the null check, just evaluate the
   // child and decrement its use count UNLESS the child is a pass-through node
   // in which case some kind of explicit test or indirect load must be generated
   // to force the null check at this point.

   TR::Node     *firstChild     = node->getFirstChild();
   TR::ILOpCode &opCode = firstChild->getOpCode();
   TR::Node *reference = NULL;
   TR::Compilation *comp = cg->comp();

   bool hasCompressedPointers = false;
   if (comp->useCompressedPointers()
         && firstChild->getOpCodeValue() == TR::l2a)
      {
      hasCompressedPointers = true;
      TR::ILOpCodes loadOp = comp->il.opCodeForIndirectLoad(TR::Int32);
      TR::ILOpCodes rdbarOp = comp->il.opCodeForIndirectReadBarrier(TR::Int32);
      TR::Node *n = firstChild;
      while ((n->getOpCodeValue() != loadOp) && (n->getOpCodeValue() != rdbarOp))
         n = n->getFirstChild();
      reference = n->getFirstChild();
      }
   else
      reference = node->getNullCheckReference();

   // TODO - If a resolve check is needed as well, the resolve must be done
   // before the null check, so that exceptions are handled in the correct
   // order.
   //
   ///// if (needsResolve)
   /////    {
   /////    ...
   /////    }

   TR::Register *trgReg = cg->evaluate(reference);
   TR::Instruction *gcPoint;

   gcPoint = TR::TreeEvaluator::generateNullTestInstructions(cg, trgReg, node);

   gcPoint->PPCNeedsGCMap(0xFFFFFFFF);

   TR::Node *n = NULL;
   if (comp->useCompressedPointers()
         && reference->getOpCodeValue() == TR::l2a)
      {
      reference->setIsNonNull(true);
      n = reference->getFirstChild();
      TR::ILOpCodes loadOp = comp->il.opCodeForIndirectLoad(TR::Int32);
      TR::ILOpCodes rdbarOp = comp->il.opCodeForIndirectReadBarrier(TR::Int32);
      while ((n->getOpCodeValue() != loadOp) && (n->getOpCodeValue() != rdbarOp))
         {
         n->setIsNonZero(true);
         n = n->getFirstChild();
         }
      n->setIsNonZero(true);
      }

   reference->setIsNonNull(true);

   cg->evaluate(firstChild);
   cg->decReferenceCount(firstChild);

   return NULL;
   }
