/*******************************************************************************
 * 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 "arm/codegen/ARMInstruction.hpp"
#include "codegen/BackingStore.hpp"
#include "codegen/CodeGenerator.hpp"
#include "codegen/CodeGeneratorUtils.hpp"
#include "codegen/GenerateInstructions.hpp"
#include "codegen/Linkage.hpp"
#include "codegen/Machine.hpp"
#include "codegen/RealRegister.hpp"
#include "codegen/Register.hpp"
#include "codegen/RegisterDependency.hpp"
#include "codegen/RegisterPair.hpp"
#include "codegen/TreeEvaluator.hpp"
#include "env/CompilerEnv.hpp"
#include "il/Node.hpp"
#include "il/Node_inlines.hpp"
#include "il/TreeTop.hpp"
#include "il/TreeTop_inlines.hpp"

#if (defined(__VFP_FP__) && !defined(__SOFTFP__))
// Helpers
static bool noFPRA = false; // The current RA *does* seem to assign FP regs.  Shall we change this to false?

static TR::Register *moveToFloatRegister(TR::Node *node, TR::Register *srcReg, TR::CodeGenerator *cg)
{
    // Move from regular registers to single floating point register
    if (srcReg->getKind() == TR_FPR) {
        TR_ASSERT(srcReg->isSinglePrecision(), "Expecting a single precision register\n");
        return srcReg;
    } else // GPR
    {
        TR::Register *trgReg = cg->allocateSinglePrecisionRegister();
        generateTrg1Src1Instruction(cg, TR::InstOpCode::fmsr, node, trgReg, srcReg);
        return trgReg;
    }
}

static TR::Register *moveToDoubleRegister(TR::Node *node, TR::Register *srcReg, TR::CodeGenerator *cg)
{
    if (srcReg->getKind() == TR_FPR) {
        TR_ASSERT(!srcReg->isSinglePrecision(), "Expecting a double precision register\n");
        return srcReg;
    } else // GPR
    {
        TR::Register *trgReg = cg->allocateRegister(TR_FPR);
        TR::RegisterPair *pair = srcReg->getRegisterPair();
        generateTrg1Src2Instruction(cg, TR::InstOpCode::fmdrr, node, trgReg, pair->getLowOrder(), pair->getHighOrder());
        return trgReg;
    }
}

static TR::Register *moveFromFloatRegister(TR::Node *node, TR::Register *srcReg, TR::CodeGenerator *cg)
{
    TR::Register *trgReg = cg->allocateRegister();
    generateTrg1Src1Instruction(cg, TR::InstOpCode::fmrs, node, trgReg, srcReg);
    return trgReg;
}

static TR::Register *moveFromDoubleRegister(TR::Node *node, TR::Register *srcReg, TR::CodeGenerator *cg)
{
    TR::Register *lowReg = cg->allocateRegister();
    TR::Register *highReg = cg->allocateRegister();
    TR::RegisterPair *trgReg = cg->allocateRegisterPair(lowReg, highReg);
    generateTrg2Src1Instruction(cg, TR::InstOpCode::fmrrd, node, lowReg, highReg, srcReg);
    return trgReg;
}

static bool resultCanStayInFloatRegister(TR::Node *checkNode, TR::Node *node)
{
    bool result = true;
    TR::Node *child = NULL;
    static bool disableVFPOpt = (feGetEnv("TR_DisableVFPOpt") != NULL);
    if (disableVFPOpt)
        return false;

    /* TODO: Memo - Below may hurt GRA when regassign for FP/Double is enabled in future. */
    if (node->getReferenceCount() > 1)
        result = false;
    for (uint16_t i = 0; i < checkNode->getNumChildren() && result; i++) {
        child = checkNode->getChild(i);
        if (child->getOpCode().isCall())
            result = false;
        else if (checkNode->getOpCodeValue() == TR::fstore && node->getOpCodeValue() == TR::fload)
            result = false;
        /*
        else if (checkNode->getOpCodeValue() == TR::dstore && node->getOpCodeValue() == TR::dload)
           result = false;
           */
        else
            result = resultCanStayInFloatRegister(child, node);
        if (!result || child == node)
            break;
    }
    return result;
}

static TR::Register *singlePrecisionEvaluator(TR::Node *node, TR::InstOpCode::Mnemonic opCode, TR::CodeGenerator *cg)
{
    TR::Node *firstChild = node->getFirstChild();
    TR::Node *secondChild = NULL;
    TR::Register *src1Reg = cg->evaluate(firstChild);
    TR::Register *src2Reg = NULL;
    TR::Register *floatTrgReg = cg->allocateSinglePrecisionRegister();
    TR::Register *trgReg = NULL;

    TR_ASSERT(node->getNumChildren() <= 2, "Not expecting more than 2 children in this node\n");
    if (node->getNumChildren() == 2) {
        secondChild = node->getSecondChild();
        src2Reg = cg->evaluate(secondChild);
        generateTrg1Src2Instruction(cg, opCode, node, floatTrgReg, moveToFloatRegister(node, src1Reg, cg),
            moveToFloatRegister(node, src2Reg, cg));
    } else // 1 child
    {
        generateTrg1Src1Instruction(cg, opCode, node, floatTrgReg, moveToFloatRegister(node, src1Reg, cg));
    }

    // Test with no RA, put values back in general registers, conforming to ABI
    if (noFPRA) {
        if (resultCanStayInFloatRegister(cg->getCurrentEvaluationTreeTop()->getNode(), node)) {
            traceMsg(cg->comp(), "Result of node %p can stay in FP reg.\n", node);
            trgReg = floatTrgReg;
        } else {
            trgReg = moveFromFloatRegister(node, floatTrgReg, cg);
            cg->stopUsingRegister(floatTrgReg);
        }
    } else
        trgReg = floatTrgReg;

    node->setRegister(trgReg);
    cg->decReferenceCount(firstChild);
    if (node->getNumChildren() == 2)
        cg->decReferenceCount(secondChild);
    return trgReg;
}

static TR::Register *doublePrecisionEvaluator(TR::Node *node, TR::InstOpCode::Mnemonic opCode, TR::CodeGenerator *cg)
{
    TR::Node *firstChild = node->getFirstChild();
    TR::Node *secondChild = NULL;
    TR::Register *src1Reg = cg->evaluate(firstChild);
    TR::Register *src2Reg = NULL;
    TR::Register *doubleTrgReg = cg->allocateRegister(TR_FPR);
    TR::Register *trgReg = NULL;

    TR_ASSERT(node->getNumChildren() <= 2, "Not expecting more than 2 children in this node\n");
    // TR_ASSERT(src1Reg->getRegisterPair(), "Expecting double value in a register pair\n");
    if (node->getNumChildren() == 2) {
        secondChild = node->getSecondChild();
        src2Reg = cg->evaluate(secondChild);
        // TR_ASSERT(src2Reg->getRegisterPair(), "Expecting double value in a register pair\n");
        generateTrg1Src2Instruction(cg, opCode, node, doubleTrgReg, moveToDoubleRegister(node, src1Reg, cg),
            moveToDoubleRegister(node, src2Reg, cg));
    } else // 1 child
    {
        generateTrg1Src1Instruction(cg, opCode, node, doubleTrgReg, moveToDoubleRegister(node, src1Reg, cg));
    }

    // Test with no RA, put values back in general registers, conforming to ABI
    if (noFPRA) {
        if (resultCanStayInFloatRegister(cg->getCurrentEvaluationTreeTop()->getNode(), node)) {
            traceMsg(cg->comp(), "Result of node %p can stay in FP reg.\n", node);
            trgReg = doubleTrgReg;
        } else {
            trgReg = moveFromDoubleRegister(node, doubleTrgReg, cg);
            cg->stopUsingRegister(doubleTrgReg);
        }
    } else
        trgReg = doubleTrgReg;

    node->setRegister(trgReg);
    cg->decReferenceCount(firstChild);
    if (node->getNumChildren() == 2)
        cg->decReferenceCount(secondChild);
    return trgReg;
}

static TR::Register *callLong2DoubleHelper(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Node *child = node->getFirstChild();
    TR::Register *srcReg = cg->evaluate(child);
    TR::RegisterDependencyConditions *dependencies
        = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(10, 10, cg->trMemory());
    int i;

    TR::Register *highReg = cg->allocateRegister();
    TR::Register *lowReg = cg->allocateRegister();
#if defined(__ARM_PCS_VFP)
    TR::Register *trgReg = cg->allocateRegister(TR_FPR);
#else
    TR::Register *trgReg = cg->allocateRegisterPair(lowReg, highReg);
#endif
    TR::Register *doubleTrgReg = NULL;

    if (cg->comp()->target().cpu.isLittleEndian()) {
        /* Little Endian */
        dependencies->addPreCondition(srcReg->getHighOrder(), TR::RealRegister::gr1);
        dependencies->addPreCondition(srcReg->getLowOrder(), TR::RealRegister::gr0);
#if defined(__ARM_PCS_VFP)
        dependencies->addPostCondition(trgReg, TR::RealRegister::fp0);
        for (i = 1; i < 8; i++) {
            TR::RealRegister::RegNum realReg = (TR::RealRegister::RegNum)((uint32_t)TR::RealRegister::fp0 + i);
            dependencies->addPostCondition(cg->allocateRegister(TR_FPR), realReg); // TODO live register
        }
        dependencies->addPostCondition(cg->allocateRegister(), TR::RealRegister::gr1); // TODO live register
        dependencies->addPostCondition(cg->allocateRegister(), TR::RealRegister::gr0); // TODO live register
#else
        dependencies->addPostCondition(trgReg->getHighOrder(), TR::RealRegister::gr1);
        dependencies->addPostCondition(trgReg->getLowOrder(), TR::RealRegister::gr0);
#endif
    } else {
        /* Big Endian */
        dependencies->addPreCondition(srcReg->getHighOrder(), TR::RealRegister::gr0);
        dependencies->addPreCondition(srcReg->getLowOrder(), TR::RealRegister::gr1);
#if defined(__ARM_PCS_VFP)
        dependencies->addPostCondition(trgReg, TR::RealRegister::fp0);
        for (i = 1; i < 8; i++) {
            TR::RealRegister::RegNum realReg = (TR::RealRegister::RegNum)((uint32_t)TR::RealRegister::fp0 + i);
            dependencies->addPostCondition(cg->allocateRegister(TR_FPR), realReg); // TODO live register
        }
        dependencies->addPostCondition(cg->allocateRegister(), TR::RealRegister::gr0); // TODO live register
        dependencies->addPostCondition(cg->allocateRegister(), TR::RealRegister::gr1); // TODO live register
#else
        dependencies->addPostCondition(trgReg->getHighOrder(), TR::RealRegister::gr0);
        dependencies->addPostCondition(trgReg->getLowOrder(), TR::RealRegister::gr1);
#endif
    }
    dependencies->stopAddingConditions();

    generateImmSymInstruction(cg, TR::InstOpCode::bl, node,
        (uintptr_t)cg->symRefTab()->findOrCreateRuntimeHelper(TR_ARMlong2Double)->getMethodAddress(), dependencies,
        cg->symRefTab()->findOrCreateRuntimeHelper(TR_ARMlong2Double));

    if (noFPRA) {
#if defined(__ARM_PCS_VFP)
        if (resultCanStayInFloatRegister(cg->getCurrentEvaluationTreeTop()->getNode(), node)) {
            traceMsg(cg->comp(), "Result of node %p can stay in FP reg.\n", node);
            doubleTrgReg = trgReg;
        } else {
            doubleTrgReg = moveFromDoubleRegister(node, trgReg, cg);
            cg->stopUsingRegister(trgReg);
        }
#else
        doubleTrgReg = trgReg;
#endif
    } else
        doubleTrgReg = trgReg;

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

    return doubleTrgReg;
}

static TR::Register *callLong2FloatHelper(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Node *child = node->getFirstChild();
    TR::Register *srcReg = cg->evaluate(child);
    TR::RegisterDependencyConditions *dependencies
        = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(10, 10, cg->trMemory());
    int i;

#if defined(__ARM_PCS_VFP)
    TR::Register *trgReg = cg->allocateSinglePrecisionRegister();
#else
    TR::Register *trgReg = cg->allocateRegister(); // Callout follows soft-float abi
#endif
    TR::Register *floatTrgReg = NULL;

    if (cg->comp()->target().cpu.isLittleEndian()) {
        /* Little Endian */
        dependencies->addPreCondition(srcReg->getHighOrder(), TR::RealRegister::gr1);
        dependencies->addPreCondition(srcReg->getLowOrder(), TR::RealRegister::gr0);

#if defined(__ARM_PCS_VFP)
        dependencies->addPostCondition(trgReg, TR::RealRegister::fp0);
        for (i = 1; i < 8; i++) {
            TR::RealRegister::RegNum realReg = (TR::RealRegister::RegNum)((uint32_t)TR::RealRegister::fp0 + i);
            dependencies->addPostCondition(cg->allocateSinglePrecisionRegister(), realReg); // TODO live register
        }
        dependencies->addPostCondition(cg->allocateRegister(), TR::RealRegister::gr1); // TODO live register
        dependencies->addPostCondition(cg->allocateRegister(), TR::RealRegister::gr0); // TODO live register
#else
        dependencies->addPostCondition(cg->allocateRegister(), TR::RealRegister::gr1); // TODO live register
        dependencies->addPostCondition(trgReg, TR::RealRegister::gr0);
#endif
    } else {
        /* Big Endian */
        dependencies->addPreCondition(srcReg->getHighOrder(), TR::RealRegister::gr0);
        dependencies->addPreCondition(srcReg->getLowOrder(), TR::RealRegister::gr1);

#if defined(__ARM_PCS_VFP)
        dependencies->addPostCondition(trgReg, TR::RealRegister::fp0);
        for (i = 1; i < 8; i++) {
            TR::RealRegister::RegNum realReg = (TR::RealRegister::RegNum)((uint32_t)TR::RealRegister::fp0 + i);
            dependencies->addPostCondition(cg->allocateSinglePrecisionRegister(), realReg); // TODO live register
        }
        dependencies->addPostCondition(cg->allocateRegister(), TR::RealRegister::gr0); // TODO live register
        dependencies->addPostCondition(cg->allocateRegister(), TR::RealRegister::gr1); // TODO live register
#else
        dependencies->addPostCondition(trgReg, TR::RealRegister::gr0);
        dependencies->addPostCondition(cg->allocateRegister(), TR::RealRegister::gr1); // TODO live register
#endif
    }
    dependencies->stopAddingConditions();

    generateImmSymInstruction(cg, TR::InstOpCode::bl, node,
        (uintptr_t)cg->symRefTab()->findOrCreateRuntimeHelper(TR_ARMlong2Float)->getMethodAddress(), dependencies,
        cg->symRefTab()->findOrCreateRuntimeHelper(TR_ARMlong2Float));

    if (noFPRA) {
#if defined(__ARM_PCS_VFP)
        if (resultCanStayInFloatRegister(cg->getCurrentEvaluationTreeTop()->getNode(), node)) {
            traceMsg(cg->comp(), "Result of node %p can stay in FP reg.\n", node);
            floatTrgReg = trgReg;
        } else {
            floatTrgReg = moveFromFloatRegister(node, trgReg, cg);
            cg->stopUsingRegister(trgReg);
        }
#else
        floatTrgReg = trgReg;
#endif
    } else
        floatTrgReg = trgReg;

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

    return floatTrgReg;
}

static TR::Register *callDouble2LongHelper(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Node *child = node->getFirstChild();
    TR::Register *doubleSrcReg = cg->evaluate(child);
    TR::Register *srcReg = NULL;
    TR::RegisterDependencyConditions *dependencies
        = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(11, 11, cg->trMemory());
    int i;
    TR::Register *highReg = cg->allocateRegister();
    TR::Register *lowReg = cg->allocateRegister();
    TR::Register *trgReg = cg->allocateRegisterPair(lowReg, highReg);

#if defined(__ARM_PCS_VFP) // hard fp
    if (doubleSrcReg->getKind() == TR_GPR) {
        srcReg = moveToDoubleRegister(node, doubleSrcReg, cg);
    } else if (doubleSrcReg->getKind() == TR_FPR) {
        srcReg = doubleSrcReg;
    } else
        TR_ASSERT(0, "Unknown register type\n");
#else // soft fp
    if (doubleSrcReg->getKind() == TR_GPR) {
        srcReg = doubleSrcReg;
    } else if (doubleSrcReg->getKind() == TR_FPR) {
        srcReg = moveFromDoubleRegister(node, doubleSrcReg, cg);
    } else
        TR_ASSERT(0, "Unknown register type\n");
#endif

    if (cg->comp()->target().cpu.isLittleEndian()) {
        /* Little Endian */

#if defined(__ARM_PCS_VFP)
        dependencies->addPreCondition(cg->allocateRegister(), TR::RealRegister::gr0); // TODO live register
        dependencies->addPreCondition(cg->allocateRegister(), TR::RealRegister::gr1); // TODO live register

        dependencies->addPreCondition(srcReg, TR::RealRegister::fp0);
        for (i = 0; i < 8; i++) {
            TR::RealRegister::RegNum realReg = (TR::RealRegister::RegNum)((uint32_t)TR::RealRegister::fp0 + i);
            dependencies->addPostCondition(cg->allocateRegister(TR_FPR), realReg); // TODO live register
        }
#else
        dependencies->addPreCondition(srcReg->getHighOrder(), TR::RealRegister::gr1);
        dependencies->addPreCondition(srcReg->getLowOrder(), TR::RealRegister::gr0);
#endif
        dependencies->addPostCondition(trgReg->getLowOrder(), TR::RealRegister::gr0);
        dependencies->addPostCondition(trgReg->getHighOrder(), TR::RealRegister::gr1);
    } else {
        /* Big Endian */
#if defined(__ARM_PCS_VFP)
        dependencies->addPreCondition(cg->allocateRegister(), TR::RealRegister::gr0); // TODO live register
        dependencies->addPreCondition(cg->allocateRegister(), TR::RealRegister::gr1); // TODO live register

        dependencies->addPreCondition(srcReg, TR::RealRegister::fp0);
        for (i = 0; i < 8; i++) {
            TR::RealRegister::RegNum realReg = (TR::RealRegister::RegNum)((uint32_t)TR::RealRegister::fp0 + i);
            dependencies->addPostCondition(cg->allocateRegister(TR_FPR), realReg); // TODO live register
        }
#else
        dependencies->addPreCondition(srcReg->getHighOrder(), TR::RealRegister::gr0);
        dependencies->addPreCondition(srcReg->getLowOrder(), TR::RealRegister::gr1);
#endif
        dependencies->addPostCondition(trgReg->getHighOrder(), TR::RealRegister::gr0);
        dependencies->addPostCondition(trgReg->getLowOrder(), TR::RealRegister::gr1);
    }
    TR::addDependency(dependencies, NULL, TR::RealRegister::gr2, TR_GPR, cg); // Block r2
    dependencies->stopAddingConditions();

    generateImmSymInstruction(cg, TR::InstOpCode::bl, node,
        (uintptr_t)cg->symRefTab()->findOrCreateRuntimeHelper(TR_ARMdouble2Long)->getMethodAddress(), dependencies,
        cg->symRefTab()->findOrCreateRuntimeHelper(TR_ARMdouble2Long));

    // dependencies->stopUsingDepRegs(cg, trgReg);

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

    return trgReg;
}

static TR::Register *callFloat2LongHelper(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Node *child = node->getFirstChild();
    TR::Register *floatSrcReg = cg->evaluate(child);
    TR::Register *srcReg = NULL;
    TR::RegisterDependencyConditions *dependencies
        = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(11, 11, cg->trMemory());
    int i;
    TR::Register *highReg = cg->allocateRegister();
    TR::Register *lowReg = cg->allocateRegister();
    TR::Register *trgReg = cg->allocateRegisterPair(lowReg, highReg);

#if defined(__ARM_PCS_VFP)
    if (floatSrcReg->getKind() == TR_GPR) {
        srcReg = moveToFloatRegister(node, floatSrcReg, cg);
    } else if (floatSrcReg->getKind() == TR_FPR) {
        srcReg = floatSrcReg;
    } else
        TR_ASSERT(0, "Unknown register type\n");
#else // soft fp
    if (floatSrcReg->getKind() == TR_GPR) {
        srcReg = floatSrcReg;
    } else if (floatSrcReg->getKind() == TR_FPR) {
        srcReg = moveFromFloatRegister(node, floatSrcReg, cg);
    } else
        TR_ASSERT(0, "Unknown register type\n");
#endif

    if (cg->comp()->target().cpu.isLittleEndian()) {
        /* Little Endian */
#if defined(__ARM_PCS_VFP)
        dependencies->addPreCondition(cg->allocateRegister(), TR::RealRegister::gr0); // TODO live register
        dependencies->addPreCondition(srcReg, TR::RealRegister::fp0);
        for (i = 0; i < 8; i++) {
            TR::RealRegister::RegNum realReg = (TR::RealRegister::RegNum)((uint32_t)TR::RealRegister::fp0 + i);
            dependencies->addPostCondition(cg->allocateSinglePrecisionRegister(), realReg); // TODO live register
        }
#else
        dependencies->addPreCondition(srcReg, TR::RealRegister::gr0);
#endif
        dependencies->addPreCondition(cg->allocateRegister(), TR::RealRegister::gr1); // TODO live register
        dependencies->addPostCondition(trgReg->getHighOrder(), TR::RealRegister::gr1);
        dependencies->addPostCondition(trgReg->getLowOrder(), TR::RealRegister::gr0);
    } else {
        /* Big Endian */
#if defined(__ARM_PCS_VFP)
        dependencies->addPreCondition(cg->allocateRegister(), TR::RealRegister::gr0); // TODO live register
        dependencies->addPreCondition(srcReg, TR::RealRegister::fp0);
        for (i = 0; i < 8; i++) {
            TR::RealRegister::RegNum realReg = (TR::RealRegister::RegNum)((uint32_t)TR::RealRegister::fp0 + i);
            dependencies->addPostCondition(cg->allocateSinglePrecisionRegister(), realReg); // TODO live register
        }
#else
        dependencies->addPreCondition(srcReg, TR::RealRegister::gr0);
#endif
        dependencies->addPreCondition(cg->allocateRegister(), TR::RealRegister::gr1); // TODO live register
        dependencies->addPostCondition(trgReg->getHighOrder(), TR::RealRegister::gr0);
        dependencies->addPostCondition(trgReg->getLowOrder(), TR::RealRegister::gr1);
    }
    TR::addDependency(dependencies, NULL, TR::RealRegister::gr2, TR_GPR, cg); // Block r2
    dependencies->stopAddingConditions();

    generateImmSymInstruction(cg, TR::InstOpCode::bl, node,
        (uintptr_t)cg->symRefTab()->findOrCreateRuntimeHelper(TR_ARMfloat2Long)->getMethodAddress(), dependencies,
        cg->symRefTab()->findOrCreateRuntimeHelper(TR_ARMfloat2Long));

    // dependencies->stopUsingDepRegs(cg, trgReg);
    // cg->stopUsingRegister(dummyReg);
    cg->decReferenceCount(child);
    cg->machine()->setLinkRegisterKilled(true);

    return trgReg;
}

static TR::Register *callDoubleRemainderHelper(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Node *firstChild = node->getFirstChild();
    TR::Node *secondChild = node->getSecondChild();
    TR::Register *src1Reg = cg->evaluate(firstChild);
    TR::Register *src2Reg = cg->evaluate(secondChild);
    TR::RegisterDependencyConditions *dependencies
        = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(10, 10, cg->trMemory());
    int i;
    TR::Register *highReg = cg->allocateRegister();
    TR::Register *lowReg = cg->allocateRegister();
#if defined(__ARM_PCS_VFP)
    TR::Register *trgReg = cg->allocateRegister(TR_FPR);
#else
    TR::Register *trgReg = cg->allocateRegisterPair(lowReg, highReg);
#endif
    TR::Register *doubleTrgReg = NULL;

#if defined(__ARM_PCS_VFP)
    if (src1Reg->getKind() == TR_GPR) {
        src1Reg = moveToDoubleRegister(node, src1Reg, cg);
    }
    if (src2Reg->getKind() == TR_GPR) {
        src2Reg = moveToDoubleRegister(node, src2Reg, cg);
    }
#else
    if (src1Reg->getKind() == TR_FPR) {
        src1Reg = moveFromDoubleRegister(node, src1Reg, cg);
    }
    if (src2Reg->getKind() == TR_FPR) {
        src2Reg = moveFromDoubleRegister(node, src2Reg, cg);
    }
#endif
    if (!noFPRA) {
        if ((src1Reg->getKind() == TR_FPR) && !cg->canClobberNodesRegister(firstChild, 0)) {
            TR::Register *tempReg = cg->allocateRegister(TR_FPR);
            generateTrg1Src1Instruction(cg, TR::InstOpCode::fcpyd, firstChild, tempReg, src1Reg);
            src1Reg = tempReg;
        }
        if ((src2Reg->getKind() == TR_FPR) && !cg->canClobberNodesRegister(secondChild, 0)) {
            TR::Register *tempReg = cg->allocateRegister(TR_FPR);
            generateTrg1Src1Instruction(cg, TR::InstOpCode::fcpyd, secondChild, tempReg, src2Reg);
            src2Reg = tempReg;
        }
    }

    if (cg->comp()->target().cpu.isLittleEndian()) {
        /* Little Endian */
#if defined(__ARM_PCS_VFP)
        TR::addDependency(dependencies, NULL, TR::RealRegister::gr1, TR_GPR, cg);
        TR::addDependency(dependencies, NULL, TR::RealRegister::gr0, TR_GPR, cg);

        dependencies->addPreCondition(src1Reg, TR::RealRegister::fp0);
        dependencies->addPreCondition(src2Reg, TR::RealRegister::fp1);

        for (i = 2; i < 8; i++) {
            TR::RealRegister::RegNum realReg = (TR::RealRegister::RegNum)((uint32_t)TR::RealRegister::fp0 + i);
            TR::addDependency(dependencies, NULL, realReg, TR_FPR, cg);
        }
        dependencies->addPostCondition(trgReg, TR::RealRegister::fp0);
        dependencies->addPostCondition(cg->allocateRegister(TR_FPR), TR::RealRegister::fp1); // TODO live register
#else
        dependencies->addPreCondition(src1Reg->getHighOrder(), TR::RealRegister::gr1);
        dependencies->addPreCondition(src1Reg->getLowOrder(), TR::RealRegister::gr0);

        TR::addDependency(dependencies, src2Reg->getLowOrder(), TR::RealRegister::gr2, TR_GPR, cg);
        TR::addDependency(dependencies, src2Reg->getHighOrder(), TR::RealRegister::gr3, TR_GPR, cg);

        dependencies->addPostCondition(highReg, TR::RealRegister::gr1);
        dependencies->addPostCondition(lowReg, TR::RealRegister::gr0);
#endif
    } else {
        /* Big Endian */
#if defined(__ARM_PCS_VFP)
        TR::addDependency(dependencies, NULL, TR::RealRegister::gr1, TR_GPR, cg);
        TR::addDependency(dependencies, NULL, TR::RealRegister::gr0, TR_GPR, cg);

        dependencies->addPreCondition(src1Reg, TR::RealRegister::fp0);
        dependencies->addPreCondition(src2Reg, TR::RealRegister::fp1);

        for (i = 2; i < 8; i++) {
            TR::RealRegister::RegNum realReg = (TR::RealRegister::RegNum)((uint32_t)TR::RealRegister::fp0 + i);
            TR::addDependency(dependencies, NULL, realReg, TR_FPR, cg);
        }
        dependencies->addPostCondition(trgReg, TR::RealRegister::fp0);
        dependencies->addPostCondition(cg->allocateRegister(TR_FPR), TR::RealRegister::fp1); // TODO live register
#else
        dependencies->addPreCondition(src1Reg->getHighOrder(), TR::RealRegister::gr0);
        dependencies->addPreCondition(src1Reg->getLowOrder(), TR::RealRegister::gr1);

        TR::addDependency(dependencies, src2Reg->getHighOrder(), TR::RealRegister::gr2, TR_GPR, cg);
        TR::addDependency(dependencies, src2Reg->getLowOrder(), TR::RealRegister::gr3, TR_GPR, cg);

        dependencies->addPostCondition(highReg, TR::RealRegister::gr0);
        dependencies->addPostCondition(lowReg, TR::RealRegister::gr1);
#endif
    }
    dependencies->stopAddingConditions();
    generateImmSymInstruction(cg, TR::InstOpCode::bl, node,
        (uintptr_t)cg->symRefTab()->findOrCreateRuntimeHelper(TR_ARMdoubleRemainder)->getMethodAddress(), dependencies,
        cg->symRefTab()->findOrCreateRuntimeHelper(TR_ARMdoubleRemainder));

    if (noFPRA) {
#if defined(__ARM_PCS_VFP)
        if (resultCanStayInFloatRegister(cg->getCurrentEvaluationTreeTop()->getNode(), node)) {
            traceMsg(cg->comp(), "Result of node %p can stay in FP reg.\n", node);
            doubleTrgReg = trgReg;
        } else {
            doubleTrgReg = moveFromDoubleRegister(node, trgReg, cg);
            cg->stopUsingRegister(trgReg);
        }
#else
        doubleTrgReg = trgReg;
#endif
    } else
        doubleTrgReg = trgReg;

    cg->decReferenceCount(firstChild);
    cg->decReferenceCount(secondChild);
    cg->machine()->setLinkRegisterKilled(true);

    return doubleTrgReg;
}

static TR::Register *callFloatRemainderHelper(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Node *firstChild = node->getFirstChild();
    TR::Node *secondChild = node->getSecondChild();
    TR::Register *src1Reg = cg->evaluate(firstChild);
    TR::Register *src2Reg = cg->evaluate(secondChild);
    TR::RegisterDependencyConditions *dependencies
        = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(11, 11, cg->trMemory());
    int i;
#if defined(__ARM_PCS_VFP)
    TR::Register *trgReg = cg->allocateSinglePrecisionRegister();
#else
    TR::Register *trgReg = cg->allocateRegister(TR_GPR);
#endif
    TR::Register *floatTrgReg = NULL;

#if defined(__ARM_PCS_VFP)
    if (src1Reg->getKind() == TR_GPR) {
        src1Reg = moveToFloatRegister(node, src1Reg, cg);
    }
    if (src2Reg->getKind() == TR_GPR) {
        src2Reg = moveToFloatRegister(node, src2Reg, cg);
    }
#else
    if (src1Reg->getKind() == TR_FPR) {
        src1Reg = moveFromFloatRegister(node, src1Reg, cg);
    }
    if (src2Reg->getKind() == TR_FPR) {
        src2Reg = moveFromFloatRegister(node, src2Reg, cg);
    }
#endif

#if defined(__ARM_PCS_VFP)
    if (!noFPRA) {
        if ((src1Reg->getKind() == TR_FPR) && !cg->canClobberNodesRegister(firstChild, 0)) {
            TR::Register *tempReg = cg->allocateRegister(TR_FPR);
            generateTrg1Src1Instruction(cg, TR::InstOpCode::fcpys, firstChild, tempReg, src1Reg);
            src1Reg = tempReg;
        }
        if ((src2Reg->getKind() == TR_FPR) && !cg->canClobberNodesRegister(secondChild, 0)) {
            TR::Register *tempReg = cg->allocateRegister(TR_FPR);
            generateTrg1Src1Instruction(cg, TR::InstOpCode::fcpys, secondChild, tempReg, src2Reg);
            src2Reg = tempReg;
        }
    }

    TR::addDependency(dependencies, NULL, TR::RealRegister::gr1, TR_GPR, cg);
    TR::addDependency(dependencies, NULL, TR::RealRegister::gr0, TR_GPR, cg);
    dependencies->addPreCondition(src1Reg, TR::RealRegister::fp0);
    dependencies->addPreCondition(src2Reg, TR::RealRegister::fs1);
    for (i = 1; i < 8; i++) {
        TR::RealRegister::RegNum realReg = (TR::RealRegister::RegNum)((uint32_t)TR::RealRegister::fp0 + i);
        TR::addDependency(dependencies, NULL, realReg, TR_FPR, cg);
    }
    dependencies->addPostCondition(trgReg, TR::RealRegister::fp0);
#else
    dependencies->addPreCondition(src1Reg, TR::RealRegister::gr0);
    dependencies->addPreCondition(src2Reg, TR::RealRegister::gr1);

    dependencies->addPostCondition(trgReg, TR::RealRegister::gr0);
    dependencies->addPostCondition(cg->allocateRegister(), TR::RealRegister::gr1); // TODO live register
#endif
    dependencies->stopAddingConditions();

    generateImmSymInstruction(cg, TR::InstOpCode::bl, node,
        (uintptr_t)cg->symRefTab()->findOrCreateRuntimeHelper(TR_ARMfloatRemainder)->getMethodAddress(), dependencies,
        cg->symRefTab()->findOrCreateRuntimeHelper(TR_ARMfloatRemainder));

    if (noFPRA) {
#if defined(__ARM_PCS_VFP)
        if (resultCanStayInFloatRegister(cg->getCurrentEvaluationTreeTop()->getNode(), node)) {
            traceMsg(cg->comp(), "Result of node %p can stay in FP reg.\n", node);
            floatTrgReg = trgReg;
        } else {
            floatTrgReg = moveFromFloatRegister(node, trgReg, cg);
            cg->stopUsingRegister(trgReg);
        }
#else
        floatTrgReg = trgReg;
#endif
    } else
        floatTrgReg = trgReg;

    cg->decReferenceCount(firstChild);
    cg->decReferenceCount(secondChild);
    cg->machine()->setLinkRegisterKilled(true);

    return floatTrgReg;
}

TR::Register *OMR::ARM::TreeEvaluator::ibits2fEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Node *child = node->getFirstChild();
    TR::Register *target = NULL;

    if (child->getRegister() == NULL && child->getReferenceCount() == 1 && child->getOpCode().isLoadVar()) {
        TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(child, 4, cg);
        if (noFPRA) {
            if (resultCanStayInFloatRegister(cg->getCurrentEvaluationTreeTop()->getNode(), node))
                traceMsg(cg->comp(), "Result of node %p can stay in FP reg (not exec.).\n", node);

            target = cg->allocateRegister();
            generateTrg1MemInstruction(cg, TR::InstOpCode::ldr, node, target, tempMR);
        } else {
            target = cg->allocateSinglePrecisionRegister();
            if (tempMR->getIndexRegister() && tempMR->getBaseRegister()) {
                TR::Register *tempReg = cg->allocateRegister();
                generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, tempReg, tempMR->getBaseRegister(),
                    tempMR->getIndexRegister());
                tempMR->decNodeReferenceCounts();

                tempMR = new (cg->trHeapMemory()) TR::MemoryReference(tempReg, 0, cg);
                generateTrg1MemInstruction(cg, TR::InstOpCode::flds, node, target, tempMR);
                cg->stopUsingRegister(tempReg);
            } else
                generateTrg1MemInstruction(cg, TR::InstOpCode::flds, node, target, tempMR);
        }
        tempMR->decNodeReferenceCounts();
    } else {
        TR::Register *srcReg = cg->evaluate(child);
        if (noFPRA) {
            target = srcReg;
        } else {
            target = moveToFloatRegister(node, srcReg, cg);
            cg->stopUsingRegister(srcReg);
        }

        cg->decReferenceCount(child);
    }
    node->setRegister(target);
    return target;
}

TR::Register *OMR::ARM::TreeEvaluator::fbits2iEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Node *child = node->getFirstChild();
    TR::Register *target = cg->allocateRegister();

    TR_ASSERT(!node->normalizeNanValues(), "Check NAN\n");
    if (child->getRegister() == NULL && child->getReferenceCount() == 1 && child->getOpCode().isLoadVar()) {
        TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(child, 4, cg);
        generateTrg1MemInstruction(cg, TR::InstOpCode::ldr, node, target, tempMR);
        tempMR->decNodeReferenceCounts();
    } else {
        TR::Register *srcReg = cg->evaluate(child);
        if (srcReg->getKind() == TR_FPR) {
            target = moveFromFloatRegister(node, srcReg, cg);
            cg->stopUsingRegister(srcReg);
        } else {
            target = srcReg;
        }
        cg->decReferenceCount(child);
    }
    node->setRegister(target);
    return target;
}

TR::Register *OMR::ARM::TreeEvaluator::lbits2dEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Node *child = node->getFirstChild();
    TR::RegisterPair *pairTarget = NULL;
    TR::Register *target = NULL;
    TR::Register *lowReg = NULL;
    TR::Register *highReg = NULL;
    TR::Compilation *comp = cg->comp();

    if (child->getRegister() == NULL && child->getReferenceCount() == 1 && child->getOpCode().isLoadVar()) {
        TR::MemoryReference *highMem, *lowMem;
        TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(child, 8, cg);
        if (noFPRA) {
            lowReg = cg->allocateRegister();
            highReg = cg->allocateRegister();
            highMem = new (cg->trHeapMemory())
                TR::MemoryReference(*tempMR, (cg->comp()->target().cpu.isBigEndian()) ? 0 : 4, 4, cg);
            generateTrg1MemInstruction(cg, TR::InstOpCode::ldr, node, highReg, highMem);
            lowMem = new (cg->trHeapMemory())
                TR::MemoryReference(*tempMR, (cg->comp()->target().cpu.isBigEndian()) ? 4 : 0, 4, cg);
            generateTrg1MemInstruction(cg, TR::InstOpCode::ldr, node, lowReg, lowMem);

            highMem->decNodeReferenceCounts();
            lowMem->decNodeReferenceCounts();
            pairTarget = cg->allocateRegisterPair(lowReg, highReg);
            node->setRegister(pairTarget);
            return pairTarget;
        } else {
            target = cg->allocateRegister(TR_FPR);
            highMem = new (cg->trHeapMemory()) TR::MemoryReference(*tempMR, 0, 8, cg);
            if (highMem->getIndexRegister() && highMem->getBaseRegister()) {
                TR::Register *tempReg = cg->allocateRegister();
                generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, tempReg, highMem->getBaseRegister(),
                    highMem->getIndexRegister());
                highMem->decNodeReferenceCounts();

                highMem = new (cg->trHeapMemory()) TR::MemoryReference(tempReg, 0, cg);
                generateTrg1MemInstruction(cg, TR::InstOpCode::fldd, node, target, highMem);
                cg->stopUsingRegister(tempReg);
            } else
                generateTrg1MemInstruction(cg, TR::InstOpCode::fldd, node, target, highMem);
            highMem->decNodeReferenceCounts();
            node->setRegister(target);
            return target;
        }
    } else {
        TR::Register *srcReg = cg->evaluate(child);
        if (noFPRA) {
            target = srcReg;
        } else {
            target = moveToDoubleRegister(node, srcReg, cg);
            cg->stopUsingRegister(srcReg);
        }

        cg->decReferenceCount(child);
        node->setRegister(target);
        return target;
    }
}

TR::Register *OMR::ARM::TreeEvaluator::dbits2lEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Node *child = node->getFirstChild();
    TR::Register *target = NULL;
    TR::Register *srcReg = NULL;
    TR::Register *lowReg = NULL;
    TR::Register *highReg = NULL;
    TR::Compilation *comp = cg->comp();

    TR_ASSERT(!node->normalizeNanValues(), "Check NAN\n");
    if (child->getRegister() == NULL && child->getReferenceCount() == 1 && child->getOpCode().isLoadVar()) {
        TR::MemoryReference *highMem, *lowMem;
        TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(child, 8, cg);
        lowReg = cg->allocateRegister();
        highReg = cg->allocateRegister();
        highMem = new (cg->trHeapMemory())
            TR::MemoryReference(*tempMR, (cg->comp()->target().cpu.isBigEndian()) ? 0 : 4, 4, cg);
        generateTrg1MemInstruction(cg, TR::InstOpCode::ldr, node, highReg, highMem);
        lowMem = new (cg->trHeapMemory())
            TR::MemoryReference(*tempMR, (cg->comp()->target().cpu.isBigEndian()) ? 4 : 0, 4, cg);
        generateTrg1MemInstruction(cg, TR::InstOpCode::ldr, node, lowReg, lowMem);

        highMem->decNodeReferenceCounts();
        lowMem->decNodeReferenceCounts();
        target = cg->allocateRegisterPair(lowReg, highReg);
        tempMR->decNodeReferenceCounts(); // Taken from PPC
    } else {
        srcReg = cg->evaluate(child);
        if (srcReg->getKind() == TR_FPR) {
            target = moveFromDoubleRegister(node, srcReg, cg);
            cg->stopUsingRegister(srcReg);
        } else {
            target = srcReg;
        }
        cg->decReferenceCount(child);
    }
    node->setRegister(target);
    return target; // returns RegisterPair.
}

TR::Register *OMR::ARM::TreeEvaluator::fconstEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // Use PC relative to get to the constant data, loading data at Snippet area is too far
    // Example:
    //    add	r4, r15, #8 (+2 instructions)
    //    flds   s0, [r15]   (Take advantage of PC+8)
    //    mov    r15, r4     (Continue, bypassing the data)
    //    .word  0x3fc00000  (Float value)
    //    fmrs   r0, s0      (if result goes back to general register)

    TR::Register *trgReg = NULL;
    TR::Register *tempReg = cg->allocateRegister();
    TR::Register *floatTrgReg = cg->allocateSinglePrecisionRegister();
    float value = node->getFloat();
    uint32_t i32 = *(int32_t *)(&value);
    TR::Compilation *comp = cg->comp();
    TR::RealRegister *machineIPReg = cg->machine()->getRealRegister(TR::RealRegister::gr15);
    TR::RegisterDependencyConditions *deps
        = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(2, 2, cg->trMemory());

    traceMsg(comp, "In fconstEvaluator %x\n", i32);
    TR::addDependency(deps, tempReg, TR::RealRegister::NoReg, TR_GPR, cg);
    if (!noFPRA) {
        TR::addDependency(deps, floatTrgReg, TR::RealRegister::NoReg, TR_FPR, cg);
    }
    deps->stopAddingConditions();

    generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::add, node, tempReg, machineIPReg, 8, 0);
    TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(machineIPReg, NULL, cg);
    generateTrg1MemInstruction(cg, TR::InstOpCode::flds, node, floatTrgReg, tempMR);
    generateTrg1Src1Instruction(cg, TR::InstOpCode::mov, node, machineIPReg, tempReg);
    // cg->stopUsingRegister(tempReg);

    // Place the constant
    generateImmInstruction(cg, TR::InstOpCode::dd, node, i32);
    // armCG(cg)->findOrCreateFloatConstant(&value, TR::Float, cursor);

    if (noFPRA) {
        if (resultCanStayInFloatRegister(cg->getCurrentEvaluationTreeTop()->getNode(), node)) {
            traceMsg(cg->comp(), "Result of node %p can stay in FP reg.\n", node);
            trgReg = floatTrgReg;
        } else {
            trgReg = moveFromFloatRegister(node, floatTrgReg, cg);
            cg->stopUsingRegister(floatTrgReg);
        }
    } else
        trgReg = floatTrgReg;

    TR::LabelSymbol *fenceLabel = TR::LabelSymbol::create(cg->trHeapMemory(), cg);
    generateLabelInstruction(cg, TR::InstOpCode::label, node, fenceLabel, deps);
    cg->stopUsingRegister(tempReg);
    node->setRegister(trgReg);
    return trgReg;
}

TR::Register *OMR::ARM::TreeEvaluator::dconstEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // Use PC relative to get to the constant data, loading data at Snippet area is too far
    // Example:
    //   add     r4, r15, #12 (Target +3 instructions)
    //   fldd    d5, [r15]    (Take advantage of PC+8)
    //   mov     r15, r4      (Continue, bypassing the data)
    //   .word   0x33333333   (lower 32 for little endian)
    //   .word   0x40033333   (upper 32 for little endian)
    //   fmrrd   r0, r1, d5   (if result goes back to general register)
    TR::Register *trgReg = NULL;
    TR::Register *tempReg = cg->allocateRegister();
    TR::Register *doubleTrgReg = cg->allocateRegister(TR_FPR);
    double value = node->getDouble();
    uint64_t i64 = (*(int64_t *)&value);
    TR::Compilation *comp = cg->comp();
    TR::RealRegister *machineIPReg = cg->machine()->getRealRegister(TR::RealRegister::gr15);
    TR::RegisterDependencyConditions *deps
        = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(2, 2, cg->trMemory());

    traceMsg(comp, "In dconstEvaluator %x\n", i64);
    TR::addDependency(deps, tempReg, TR::RealRegister::NoReg, TR_GPR, cg);
    if (!noFPRA) {
        TR::addDependency(deps, doubleTrgReg, TR::RealRegister::NoReg, TR_FPR, cg);
    }
    deps->stopAddingConditions();

    generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::add, node, tempReg, machineIPReg, 12, 0);
    TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(machineIPReg, NULL, cg);
    generateTrg1MemInstruction(cg, TR::InstOpCode::fldd, node, doubleTrgReg, tempMR);
    generateTrg1Src1Instruction(cg, TR::InstOpCode::mov, node, machineIPReg, tempReg);
    // cg->stopUsingRegister(tempReg);

    // Place the constant
    // armCG(cg)->findOrCreateFloatConstant(&value, TR::Double, high, low);
    if (cg->comp()->target().cpu.isLittleEndian()) {
        generateImmInstruction(cg, TR::InstOpCode::dd, node, (int32_t)i64);
        generateImmInstruction(cg, TR::InstOpCode::dd, node, (int32_t)((i64 >> 32) & 0xffffffff));
    } else {
        generateImmInstruction(cg, TR::InstOpCode::dd, node, (int32_t)((i64 >> 32) & 0xffffffff));
        generateImmInstruction(cg, TR::InstOpCode::dd, node, (int32_t)i64);
    }

    if (noFPRA) {
        if (resultCanStayInFloatRegister(cg->getCurrentEvaluationTreeTop()->getNode(), node)) {
            traceMsg(comp, "Result of node %p can stay in FP reg.\n", node);
            trgReg = doubleTrgReg;
        } else {
            trgReg = moveFromDoubleRegister(node, doubleTrgReg, cg);
            cg->stopUsingRegister(doubleTrgReg);
        }
    } else
        trgReg = doubleTrgReg;

    TR::LabelSymbol *fenceLabel = TR::LabelSymbol::create(cg->trHeapMemory(), cg);
    generateLabelInstruction(cg, TR::InstOpCode::label, node, fenceLabel, deps);
    cg->stopUsingRegister(tempReg);
    node->setRegister(trgReg);
    return trgReg;
}

// also handles TR::floadi
TR::Register *OMR::ARM::TreeEvaluator::floadEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Register *trgReg = NULL;
// #define STAYINFP
#ifdef STAYINFP
    TR::Register *floatTrgReg = cg->allocateSinglePrecisionRegister();
#endif
    TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(node, 4, cg);

#ifdef STAYINFP
    if (tempMR->getIndexRegister() && tempMR->getBaseRegister()) {
        TR::Register *tempReg = cg->allocateRegister();
        generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, tempReg, tempMR->getBaseRegister(),
            tempMR->getIndexRegister());
        tempMR->decNodeReferenceCounts();
        tempMR = new (cg->trHeapMemory()) TR::MemoryReference(tempReg, 0, cg);
        generateTrg1MemInstruction(cg, TR::InstOpCode::flds, node, floatTrgReg, tempMR);
        cg->stopUsingRegister(tempReg);
    } else
        generateTrg1MemInstruction(cg, TR::InstOpCode::flds, node, floatTrgReg, tempMR);
#endif

    if (noFPRA) {
        if (resultCanStayInFloatRegister(cg->getCurrentEvaluationTreeTop()->getNode(), node)) {
            traceMsg(cg->comp(), "Result of node %p can stay in FP reg in fload.\n", node);
            // This seem to break things.. trgReg = floatTrgReg;
#ifdef STAYINFP
            trgReg = floatTrgReg;
#else
            // Load directly to a general register
            trgReg = cg->allocateRegister();
            generateTrg1MemInstruction(cg, TR::InstOpCode::ldr, node, trgReg, tempMR);
#endif
        } else {
            // Load directly to a general register
            trgReg = cg->allocateRegister();
            generateTrg1MemInstruction(cg, TR::InstOpCode::ldr, node, trgReg, tempMR);
        }
    } else {
        TR::Register *floatTrgReg = cg->allocateSinglePrecisionRegister();
        if (tempMR->getIndexRegister() && tempMR->getBaseRegister()) {
            TR::Register *tempReg = cg->allocateRegister();
            generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, tempReg, tempMR->getBaseRegister(),
                tempMR->getIndexRegister());
            tempMR->decNodeReferenceCounts();

            tempMR = new (cg->trHeapMemory()) TR::MemoryReference(tempReg, 0, cg);
            generateTrg1MemInstruction(cg, TR::InstOpCode::flds, node, floatTrgReg, tempMR);
            cg->stopUsingRegister(tempReg);
        } else
            generateTrg1MemInstruction(cg, TR::InstOpCode::flds, node, floatTrgReg, tempMR);
        trgReg = floatTrgReg;
    }

    if (node->getSymbolReference()->getSymbol()->isAtLeastOrStrongerThanAcquireRelease() && cg->comp()->target().isSMP()
        && cg->comp()->target().cpu.id() != TR_DefaultARMProcessor) {
        generateInstruction(cg,
            (cg->comp()->target().cpu.id() == TR_ARMv6) ? TR::InstOpCode::dmb_v6 : TR::InstOpCode::dmb, node);
    }
    tempMR->decNodeReferenceCounts();
    node->setRegister(trgReg);
    return trgReg;
}

// also handles TR::dloadi
TR::Register *OMR::ARM::TreeEvaluator::dloadEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Register *trgReg = NULL;
    TR::Register *doubleTrgReg = cg->allocateRegister(TR_FPR);
    TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(node, 8, cg);

    if (tempMR->getIndexRegister() && tempMR->getBaseRegister()) {
        TR::Register *tempReg = cg->allocateRegister();
        generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, tempReg, tempMR->getBaseRegister(),
            tempMR->getIndexRegister());
        tempMR->decNodeReferenceCounts();

        tempMR = new (cg->trHeapMemory()) TR::MemoryReference(tempReg, 0, cg);
        generateTrg1MemInstruction(cg, TR::InstOpCode::fldd, node, doubleTrgReg, tempMR);
        cg->stopUsingRegister(tempReg);
    } else
        generateTrg1MemInstruction(cg, TR::InstOpCode::fldd, node, doubleTrgReg, tempMR);

    if (noFPRA) {
        if (resultCanStayInFloatRegister(cg->getCurrentEvaluationTreeTop()->getNode(), node)) {
            traceMsg(cg->comp(), "Result of node %p can stay in FP reg in dload.\n", node);
            trgReg = doubleTrgReg;
        } else {
            trgReg = moveFromDoubleRegister(node, doubleTrgReg, cg);
            cg->stopUsingRegister(doubleTrgReg);
        }
    } else {
        trgReg = doubleTrgReg;
    }

    if (node->getSymbolReference()->getSymbol()->isAtLeastOrStrongerThanAcquireRelease() && cg->comp()->target().isSMP()
        && cg->comp()->target().cpu.id() != TR_DefaultARMProcessor) {
        generateInstruction(cg,
            (cg->comp()->target().cpu.id() == TR_ARMv6) ? TR::InstOpCode::dmb_v6 : TR::InstOpCode::dmb, node);
    }
    tempMR->decNodeReferenceCounts();
    node->setRegister(trgReg);
    return trgReg;
}

TR::Register *OMR::ARM::TreeEvaluator::fstoreEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Node *firstChild = node->getFirstChild();
    TR::Register *sourceReg = cg->evaluate(firstChild);
    TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(node, 4, cg);

    if (node->getSymbolReference()->getSymbol()->isAtLeastOrStrongerThanAcquireRelease() && cg->comp()->target().isSMP()
        && cg->comp()->target().cpu.id() != TR_DefaultARMProcessor) {
        generateInstruction(cg,
            (cg->comp()->target().cpu.id() == TR_ARMv6) ? TR::InstOpCode::dmb_v6 : TR::InstOpCode::dmb, node);
    }
    if (noFPRA) {
        if (sourceReg->getKind() == TR_GPR) {
            // sourceReg is in a general register
            generateMemSrc1Instruction(cg, TR::InstOpCode::str, node, tempMR, sourceReg);
        } else if (sourceReg->getKind() == TR_FPR) {
            if (tempMR->getIndexRegister() && tempMR->getBaseRegister()) {
                TR::Register *tempReg = cg->allocateRegister();
                generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, tempReg, tempMR->getBaseRegister(),
                    tempMR->getIndexRegister());
                tempMR->setIndexRegister(NULL);
                tempMR->setIndexNode(NULL);
                tempMR->setBaseRegister(tempReg);
                tempMR->setBaseNode(node);
                generateMemSrc1Instruction(cg, TR::InstOpCode::fsts, node, tempMR, sourceReg);
                cg->stopUsingRegister(tempReg);
            } else
                generateMemSrc1Instruction(cg, TR::InstOpCode::fsts, node, tempMR, sourceReg);
        }
    } else {
        if (tempMR->getIndexRegister() && tempMR->getBaseRegister()) {
            TR::Register *tempReg = cg->allocateRegister();
            generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, tempReg, tempMR->getBaseRegister(),
                tempMR->getIndexRegister());
            tempMR->setIndexRegister(NULL);
            tempMR->setIndexNode(NULL);
            tempMR->setBaseRegister(tempReg);
            tempMR->setBaseNode(node);
            generateMemSrc1Instruction(cg, TR::InstOpCode::fsts, node, tempMR, sourceReg);
            cg->stopUsingRegister(tempReg);
        } else
            generateMemSrc1Instruction(cg, TR::InstOpCode::fsts, node, tempMR, sourceReg);
    }

    cg->decReferenceCount(firstChild);
    tempMR->decNodeReferenceCounts();
    return NULL;
}

TR::Register *OMR::ARM::TreeEvaluator::dstoreEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Node *firstChild = node->getFirstChild();
    TR::Register *sourceReg = cg->evaluate(firstChild);
    bool isUnresolved = node->getSymbolReference()->isUnresolved();

    if (node->getSymbolReference()->getSymbol()->isAtLeastOrStrongerThanAcquireRelease() && cg->comp()->target().isSMP()
        && cg->comp()->target().cpu.id() != TR_DefaultARMProcessor) {
        generateInstruction(cg,
            (cg->comp()->target().cpu.id() == TR_ARMv6) ? TR::InstOpCode::dmb_v6 : TR::InstOpCode::dmb, node);
    }
    if (noFPRA) {
        if (sourceReg->getKind() == TR_GPR) {
            // sourceReg is in general registers, mimic a lstore
            TR::MemoryReference *lowMR = new (cg->trHeapMemory()) TR::MemoryReference(node, 4, cg);
            TR::MemoryReference *highMR = new (cg->trHeapMemory()) TR::MemoryReference(*lowMR, 4, 4, cg);
            if (cg->comp()->target().cpu.isBigEndian()) {
                generateMemSrc1Instruction(cg, TR::InstOpCode::str, node, lowMR, sourceReg->getHighOrder());
                generateMemSrc1Instruction(cg, TR::InstOpCode::str, node, highMR, sourceReg->getLowOrder());
            } else {
                generateMemSrc1Instruction(cg, TR::InstOpCode::str, node, lowMR, sourceReg->getLowOrder());
                generateMemSrc1Instruction(cg, TR::InstOpCode::str, node, highMR, sourceReg->getHighOrder());
            }
        } else if (sourceReg->getKind() == TR_FPR) {
            TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(node, 8, cg);
            if (tempMR->getIndexRegister() && tempMR->getBaseRegister()) {
                TR::Register *tempReg = cg->allocateRegister();
                generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, tempReg, tempMR->getBaseRegister(),
                    tempMR->getIndexRegister());
                tempMR->setIndexRegister(NULL);
                tempMR->setIndexNode(NULL);
                tempMR->setBaseRegister(tempReg);
                tempMR->setBaseNode(node);
                generateMemSrc1Instruction(cg, TR::InstOpCode::fstd, node, tempMR, sourceReg);
                cg->stopUsingRegister(tempReg);
            } else
                generateMemSrc1Instruction(cg, TR::InstOpCode::fstd, node, tempMR, sourceReg);
            tempMR->decNodeReferenceCounts();
        } else
            TR_ASSERT(0, "Unknown register type\n");
    } else {
        TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(node, 8, cg);
        if (tempMR->getIndexRegister() && tempMR->getBaseRegister()) {
            TR::Register *tempReg = cg->allocateRegister();
            generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, tempReg, tempMR->getBaseRegister(),
                tempMR->getIndexRegister());
            tempMR->setIndexRegister(NULL);
            tempMR->setIndexNode(NULL);
            tempMR->setBaseRegister(tempReg);
            tempMR->setBaseNode(node);
            generateMemSrc1Instruction(cg, TR::InstOpCode::fstd, node, tempMR, sourceReg);
            cg->stopUsingRegister(tempReg);
        } else
            generateMemSrc1Instruction(cg, TR::InstOpCode::fstd, node, tempMR, sourceReg);
        tempMR->decNodeReferenceCounts();
    }
    cg->decReferenceCount(firstChild);
    return NULL;
}

TR::Register *OMR::ARM::TreeEvaluator::fstoreiEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Node *secondChild = node->getSecondChild();
    TR::Register *sourceReg = cg->evaluate(secondChild);
    TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(node, 4, cg);

    if (node->getSymbolReference()->getSymbol()->isAtLeastOrStrongerThanAcquireRelease() && cg->comp()->target().isSMP()
        && cg->comp()->target().cpu.id() != TR_DefaultARMProcessor) {
        generateInstruction(cg,
            (cg->comp()->target().cpu.id() == TR_ARMv6) ? TR::InstOpCode::dmb_v6 : TR::InstOpCode::dmb, node);
    }
    if (noFPRA) {
        if (sourceReg->getKind() == TR_GPR) {
            // sourceReg is in a general register
            generateMemSrc1Instruction(cg, TR::InstOpCode::str, node, tempMR, sourceReg);
        } else if (sourceReg->getKind() == TR_FPR) {
            if (tempMR->getIndexRegister() && tempMR->getBaseRegister()) {
                TR::Register *tempReg = cg->allocateRegister();
                generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, tempReg, tempMR->getBaseRegister(),
                    tempMR->getIndexRegister());
                tempMR->setIndexRegister(NULL);
                tempMR->setIndexNode(NULL);
                tempMR->setBaseRegister(tempReg);
                // tempMR->setBaseNode(node);
                generateMemSrc1Instruction(cg, TR::InstOpCode::fsts, node, tempMR, sourceReg);
                cg->stopUsingRegister(tempReg);
            } else
                generateMemSrc1Instruction(cg, TR::InstOpCode::fsts, node, tempMR, sourceReg);
        }
    } else {
        if (tempMR->getIndexRegister() && tempMR->getBaseRegister()) {
            TR::Register *tempReg = cg->allocateRegister();
            generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, tempReg, tempMR->getBaseRegister(),
                tempMR->getIndexRegister());
            tempMR->setIndexRegister(NULL);
            tempMR->setIndexNode(NULL);
            tempMR->setBaseRegister(tempReg);
            generateMemSrc1Instruction(cg, TR::InstOpCode::fsts, node, tempMR, sourceReg);
            cg->stopUsingRegister(tempReg);
        } else
            generateMemSrc1Instruction(cg, TR::InstOpCode::fsts, node, tempMR, sourceReg);
    }

    cg->decReferenceCount(secondChild);
    tempMR->decNodeReferenceCounts();
    return NULL;
}

TR::Register *OMR::ARM::TreeEvaluator::dstoreiEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Node *secondChild = node->getSecondChild();
    TR::Register *sourceReg = cg->evaluate(secondChild);

    if (node->getSymbolReference()->getSymbol()->isAtLeastOrStrongerThanAcquireRelease() && cg->comp()->target().isSMP()
        && cg->comp()->target().cpu.id() != TR_DefaultARMProcessor) {
        generateInstruction(cg,
            (cg->comp()->target().cpu.id() == TR_ARMv6) ? TR::InstOpCode::dmb_v6 : TR::InstOpCode::dmb, node);
    }

    if (noFPRA) {
        if (sourceReg->getKind() == TR_GPR) {
            // sourceReg is in general registers, mimic a lstore
            TR::MemoryReference *lowMR = new (cg->trHeapMemory()) TR::MemoryReference(node, 4, cg);
            TR::MemoryReference *highMR = new (cg->trHeapMemory()) TR::MemoryReference(*lowMR, 4, 4, cg);
            if (cg->comp()->target().cpu.isBigEndian()) {
                generateMemSrc1Instruction(cg, TR::InstOpCode::str, node, lowMR, sourceReg->getHighOrder());
                generateMemSrc1Instruction(cg, TR::InstOpCode::str, node, highMR, sourceReg->getLowOrder());
            } else {
                generateMemSrc1Instruction(cg, TR::InstOpCode::str, node, lowMR, sourceReg->getLowOrder());
                generateMemSrc1Instruction(cg, TR::InstOpCode::str, node, highMR, sourceReg->getHighOrder());
            }
        } else if (sourceReg->getKind() == TR_FPR) {
            TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(node, 8, cg);
            if (tempMR->getIndexRegister() && tempMR->getBaseRegister()) {
                TR::Register *tempReg = cg->allocateRegister();
                generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, tempReg, tempMR->getBaseRegister(),
                    tempMR->getIndexRegister());
                tempMR->setIndexRegister(NULL);
                tempMR->setIndexNode(NULL);
                tempMR->setBaseRegister(tempReg);
                generateMemSrc1Instruction(cg, TR::InstOpCode::fstd, node, tempMR, sourceReg);
                cg->stopUsingRegister(tempReg);
            } else
                generateMemSrc1Instruction(cg, TR::InstOpCode::fstd, node, tempMR, sourceReg);
            tempMR->decNodeReferenceCounts();
        } else
            TR_ASSERT(0, "Unknown register type\n");
    } else {
        TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(node, 8, cg);
        if (tempMR->getIndexRegister() && tempMR->getBaseRegister()) {
            TR::Register *tempReg = cg->allocateRegister();
            generateTrg1Src2Instruction(cg, TR::InstOpCode::add, node, tempReg, tempMR->getBaseRegister(),
                tempMR->getIndexRegister());
            tempMR->setIndexRegister(NULL);
            tempMR->setIndexNode(NULL);
            tempMR->setBaseRegister(tempReg);
            generateMemSrc1Instruction(cg, TR::InstOpCode::fstd, node, tempMR, sourceReg);
            cg->stopUsingRegister(tempReg);
        } else
            generateMemSrc1Instruction(cg, TR::InstOpCode::fstd, node, tempMR, sourceReg);
        tempMR->decNodeReferenceCounts();
    }
    cg->decReferenceCount(secondChild);
    return NULL;
}

TR::Register *OMR::ARM::TreeEvaluator::freturnEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // SOFT-ABI uses general registers to return floats
    TR::Register *returnRegister = cg->evaluate(node->getFirstChild());

    TR::RegisterDependencyConditions *deps
        = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(1, 1, cg->trMemory());
    if (returnRegister->getKind() == TR_GPR) {
        TR::addDependency(deps, returnRegister, cg->getProperties().getIntegerReturnRegister(), TR_GPR, cg);
    } else if (returnRegister->getKind() == TR_FPR) {
        TR::Register *temp = moveFromFloatRegister(node, returnRegister, cg);
        TR::addDependency(deps, temp, cg->getProperties().getIntegerReturnRegister(), TR_GPR, cg);
    } else
        TR_ASSERT(0, "Unknown register type\n");
    generateAdminInstruction(cg, TR::InstOpCode::retn, node, deps);
    cg->comp()->setReturnInfo(TR_FloatReturn);
    return NULL;
}

TR::Register *OMR::ARM::TreeEvaluator::dreturnEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Register *returnRegister = cg->evaluate(node->getFirstChild());
    TR::RegisterDependencyConditions *deps
        = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(2, 2, cg->trMemory());

    if (returnRegister->getKind() == TR_GPR) {
        TR::addDependency(deps, returnRegister->getLowOrder(), cg->getProperties().getLongLowReturnRegister(), TR_GPR,
            cg);
        TR::addDependency(deps, returnRegister->getHighOrder(), cg->getProperties().getLongHighReturnRegister(), TR_GPR,
            cg);
    } else if (returnRegister->getKind() == TR_FPR) {
        TR::Register *temp = moveFromDoubleRegister(node, returnRegister, cg);
        TR::addDependency(deps, temp->getLowOrder(), cg->getProperties().getLongLowReturnRegister(), TR_GPR, cg);
        TR::addDependency(deps, temp->getHighOrder(), cg->getProperties().getLongHighReturnRegister(), TR_GPR, cg);
    } else
        TR_ASSERT(0, "Unknown register type\n");
    generateAdminInstruction(cg, TR::InstOpCode::retn, node, deps);
    cg->comp()->setReturnInfo(TR_DoubleReturn);
    return NULL;
}

static bool isFPStrictMul(TR::Node *node, TR::Compilation *comp)
{
    if (!node->getOpCode().isMul() || !node->isFPStrictCompliant() || node->getRegister())
        return false;

    if (node->getReferenceCount() < 2 && node->getRegister() == NULL)
        return true;

    node->setIsFPStrictCompliant(false); // need to set this otherwise children get incorrectly bumped
    return false;
}

/** Generate a fused multiply add from the tree (A * B) + C, where addNode is the + node
 * and mulNode the * subtree.  Not supporting noFPRA at this time
 */
static TR::Register *generateFusedMultiplyAdd(TR::Node *addNode, TR::InstOpCode::Mnemonic OpCode, TR::CodeGenerator *cg)
{
    TR::Node *mulNode = addNode->getFirstChild();
    TR::Node *addChild = addNode->getSecondChild();

    if (!isFPStrictMul(mulNode, cg->comp())) {
        addChild = addNode->getFirstChild();
        mulNode = addNode->getSecondChild();
    }

    TR_ASSERT(mulNode->getReferenceCount() < 2, "Mul node 0x%p reference count %d >= 2\n", mulNode,
        mulNode->getReferenceCount());
    TR_ASSERT(mulNode->getOpCode().isMul(), "Unexpected op!=mul %p\n", mulNode);
    TR_ASSERT(mulNode->isFPStrictCompliant(), "mul node %p is not fpStrict Compliant\n", mulNode);
    TR::Register *source1Register = cg->evaluate(mulNode->getFirstChild());
    TR::Register *source2Register = cg->evaluate(mulNode->getSecondChild());
    TR::Register *source3Register = cg->evaluate(addChild);

    // Result is in the put back to source3Register
    TR::Register *trgRegister = source3Register;
    if (addNode->getDataType() == TR::Float) {
        if (noFPRA) {
            TR::Register *floatTrgReg = cg->allocateSinglePrecisionRegister();
            generateTrg1Src2Instruction(cg, OpCode, addNode, floatTrgReg,
                moveToFloatRegister(addNode, source1Register, cg), moveToFloatRegister(addNode, source2Register, cg));
            generateTrg1Src1Instruction(cg, TR::InstOpCode::fmrs, addNode, trgRegister, floatTrgReg);
            cg->stopUsingRegister(floatTrgReg);
        } else {
            generateTrg1Src2Instruction(cg, OpCode, addNode, trgRegister, source1Register, source2Register);
        }
    } else {
        // TR::Double
        if (noFPRA) {
            TR::Register *doubleTrgReg = cg->allocateRegister(TR_FPR);
            generateTrg1Src2Instruction(cg, OpCode, addNode, doubleTrgReg,
                moveToDoubleRegister(addNode, source1Register, cg), moveToDoubleRegister(addNode, source2Register, cg));
            generateTrg2Src1Instruction(cg, TR::InstOpCode::fmrrd, addNode, trgRegister->getLowOrder(),
                trgRegister->getHighOrder(), doubleTrgReg);
            cg->stopUsingRegister(doubleTrgReg);
        } else {
            generateTrg1Src2Instruction(cg, OpCode, addNode, trgRegister, source1Register, source2Register);
        }
    }

    addNode->setRegister(trgRegister);
    cg->decReferenceCount(mulNode->getFirstChild());
    cg->decReferenceCount(mulNode->getSecondChild());
    cg->decReferenceCount(mulNode); // don't forget this guy!
    // cg->decReferenceCount(addChild);
    return trgRegister;
}

TR::Register *OMR::ARM::TreeEvaluator::faddEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // TODO: Check conditions to use fmacs if possible
    TR::Compilation *comp = cg->comp();
    TR::Register *result = NULL;
    if (((isFPStrictMul(node->getFirstChild(), comp) && (node->getSecondChild()->getReferenceCount() == 1))
            || (isFPStrictMul(node->getSecondChild(), comp) && (node->getFirstChild()->getReferenceCount() == 1)))
        && performTransformation(comp, "O^O Changing [%p] to fmacs\n", node)) {
        result = generateFusedMultiplyAdd(node, TR::InstOpCode::fmacs, cg);
    } else {
        result = singlePrecisionEvaluator(node, TR::InstOpCode::fadds, cg);
    }
    return result;
}

TR::Register *OMR::ARM::TreeEvaluator::daddEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // TODO: Check conditions to use fmacd if possible
    TR::Register *result = NULL;
    TR::Compilation *comp = cg->comp();
    if (((isFPStrictMul(node->getFirstChild(), comp) && (node->getSecondChild()->getReferenceCount() == 1))
            || (isFPStrictMul(node->getSecondChild(), comp) && (node->getFirstChild()->getReferenceCount() == 1)))
        && performTransformation(comp, "O^O Changing [%p] to fmacd\n", node)) {
        result = generateFusedMultiplyAdd(node, TR::InstOpCode::fmacd, cg);
    } else {
        result = doublePrecisionEvaluator(node, TR::InstOpCode::faddd, cg);
    }
    return result;
}

TR::Register *OMR::ARM::TreeEvaluator::dsubEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // TODO: Check conditions to use fmscd if possible
    TR::Compilation *comp = cg->comp();
    TR::Register *result = NULL;
    if (isFPStrictMul(node->getFirstChild(), comp) && (node->getSecondChild()->getReferenceCount() == 1)
        && performTransformation(comp, "O^O Changing [%p] to fmscd\n", node)) {
        result = generateFusedMultiplyAdd(node, TR::InstOpCode::fmscd, cg);
    } else if (isFPStrictMul(node->getSecondChild(), comp) && (node->getFirstChild()->getReferenceCount() == 1)
        && performTransformation(comp, "O^O Changing [%p] to fnmacd\n", node)) {
        result = generateFusedMultiplyAdd(node, TR::InstOpCode::fnmacd, cg);
    } else {
        result = doublePrecisionEvaluator(node, TR::InstOpCode::fsubd, cg);
    }
    return result;
}

TR::Register *OMR::ARM::TreeEvaluator::fsubEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // TODO: Check conditions to use fmscs if possible
    TR::Compilation *comp = cg->comp();
    TR::Register *result = NULL;
    if (isFPStrictMul(node->getFirstChild(), comp) && (node->getSecondChild()->getReferenceCount() == 1)
        && performTransformation(comp, "O^O Changing [%p] to fmscs\n", node)) {
        result = generateFusedMultiplyAdd(node, TR::InstOpCode::fmscs, cg);
    } else if (isFPStrictMul(node->getSecondChild(), comp) && (node->getFirstChild()->getReferenceCount() == 1)
        && performTransformation(comp, "O^O Changing [%p] to fnmacs\n", node)) {
        result = generateFusedMultiplyAdd(node, TR::InstOpCode::fnmacs, cg);
    } else {
        result = singlePrecisionEvaluator(node, TR::InstOpCode::fsubs, cg);
    }
    return result;
}

TR::Register *OMR::ARM::TreeEvaluator::fmulEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    return singlePrecisionEvaluator(node, TR::InstOpCode::fmuls, cg);
}

TR::Register *OMR::ARM::TreeEvaluator::dmulEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    return doublePrecisionEvaluator(node, TR::InstOpCode::fmuld, cg);
}

TR::Register *OMR::ARM::TreeEvaluator::fdivEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    return singlePrecisionEvaluator(node, TR::InstOpCode::fdivs, cg);
}

TR::Register *OMR::ARM::TreeEvaluator::ddivEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    return doublePrecisionEvaluator(node, TR::InstOpCode::fdivd, cg);
}

TR::Register *OMR::ARM::TreeEvaluator::fremEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Register *trgReg = callFloatRemainderHelper(node, cg);
    node->setRegister(trgReg);
    return trgReg;
}

TR::Register *OMR::ARM::TreeEvaluator::dremEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Register *trgReg = callDoubleRemainderHelper(node, cg);
    node->setRegister(trgReg);
    return trgReg;
}

TR::Register *OMR::ARM::TreeEvaluator::fabsEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    return singlePrecisionEvaluator(node, TR::InstOpCode::fabss, cg);
}

TR::Register *OMR::ARM::TreeEvaluator::dabsEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    return doublePrecisionEvaluator(node, TR::InstOpCode::fabsd, cg);
}

TR::Register *OMR::ARM::TreeEvaluator::fnegEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // TODO: Check tree for possibile combinations
    TR::Register *result = NULL;
    TR::Node *firstChild = node->getFirstChild();
    bool isAdd = firstChild->getOpCode().isAdd();
    TR::Compilation *comp = cg->comp();

    if (firstChild->getReferenceCount() < 2 && !firstChild->getRegister() && isAdd) {
        // fneg(node) -> fadd(firstChild) -> a multiply in one of the children
        if (((isFPStrictMul(firstChild->getFirstChild(), comp)
                 && firstChild->getSecondChild()->getReferenceCount() == 1)
                || (isFPStrictMul(firstChild->getSecondChild(), comp)
                    && firstChild->getFirstChild()->getReferenceCount() == 1))
            && performTransformation(comp, "O^O Changing [%p] to fnmscs\n", node)) {
            result = generateFusedMultiplyAdd(node, TR::InstOpCode::fnmscs, cg);
            firstChild->unsetRegister(); // unset as the first child isn't the result node
        } else {
            result = singlePrecisionEvaluator(node, TR::InstOpCode::fnegs, cg);
        }
    } else if (isFPStrictMul(firstChild, comp) && firstChild->getReferenceCount() < 2 && !firstChild->getRegister()
        && performTransformation(comp, "O^O Changing [%p] to fnmuls\n", node)) {
        // fneg(node) -> fmul(firstChild)
        TR::Register *floatTrgReg = cg->allocateSinglePrecisionRegister();
        TR::Register *src1Register = cg->evaluate(firstChild->getFirstChild());
        TR::Register *src2Register = cg->evaluate(firstChild->getSecondChild());
        if (noFPRA) {
            generateTrg1Src2Instruction(cg, TR::InstOpCode::fnmuls, node, floatTrgReg,
                moveToFloatRegister(node, src1Register, cg), moveToFloatRegister(node, src2Register, cg));
            result = moveFromFloatRegister(node, floatTrgReg, cg);
            cg->stopUsingRegister(floatTrgReg);
        } else {
            generateTrg1Src2Instruction(cg, TR::InstOpCode::fnmuls, node, floatTrgReg, src1Register, src2Register);
            result = floatTrgReg;
        }
        cg->decReferenceCount(firstChild);
    } else {
        result = singlePrecisionEvaluator(node, TR::InstOpCode::fnegs, cg);
    }
    node->setRegister(result);
    return result;
}

TR::Register *OMR::ARM::TreeEvaluator::dnegEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // TODO: Check tree for possible combinations
    TR::Register *result = NULL;
    TR::Node *firstChild = node->getFirstChild();
    bool isAdd = firstChild->getOpCode().isAdd();
    TR::Compilation *comp = cg->comp();

    if (firstChild->getReferenceCount() < 2 && !firstChild->getRegister() && isAdd) {
        // dneg(node) -> dadd(firstChild) -> a multiply in one of the children
        if (((isFPStrictMul(firstChild->getFirstChild(), comp)
                 && firstChild->getSecondChild()->getReferenceCount() == 1)
                || (isFPStrictMul(firstChild->getSecondChild(), comp)
                    && firstChild->getFirstChild()->getReferenceCount() == 1))
            && performTransformation(comp, "O^O Changing [%p] to fnmscd\n", node)) {
            result = generateFusedMultiplyAdd(node, TR::InstOpCode::fnmscd, cg);
            firstChild->unsetRegister(); // unset as the first child isn't the result node
        } else {
            result = doublePrecisionEvaluator(node, TR::InstOpCode::fnegd, cg);
        }
    } else if (isFPStrictMul(firstChild, comp) && firstChild->getReferenceCount() < 2 && !firstChild->getRegister()
        && performTransformation(comp, "O^O Changing [%p] to fnmuld\n", node)) {
        // fneg(node) -> fmul(firstChild)
        TR::Register *doubleTrgReg = cg->allocateRegister(TR_FPR);
        TR::Register *src1Register = cg->evaluate(firstChild->getFirstChild());
        TR::Register *src2Register = cg->evaluate(firstChild->getSecondChild());
        if (noFPRA) {
            generateTrg1Src2Instruction(cg, TR::InstOpCode::fnmuld, node, doubleTrgReg,
                moveToDoubleRegister(node, src1Register, cg), moveToDoubleRegister(node, src2Register, cg));
            result = moveFromDoubleRegister(node, doubleTrgReg, cg);
            cg->stopUsingRegister(doubleTrgReg);
        } else {
            generateTrg1Src2Instruction(cg, TR::InstOpCode::fnmuld, node, doubleTrgReg, src1Register, src2Register);
            result = doubleTrgReg;
        }
        cg->decReferenceCount(firstChild);
    } else {
        result = doublePrecisionEvaluator(node, TR::InstOpCode::fnegd, cg);
    }
    node->setRegister(result);
    return result;
}

TR::Register *OMR::ARM::TreeEvaluator::i2fEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // Support iu2f, b2f, s2f and su2f
    TR::Node *firstChild = node->getFirstChild();
    TR::Register *src1Reg = NULL;
    TR::Register *trgReg = NULL;
    TR::InstOpCode::Mnemonic opcode
        = (node->getOpCodeValue() == TR::iu2f) ? TR::InstOpCode::fuitos : TR::InstOpCode::fsitos;

    if (firstChild->getReferenceCount() == 1 && firstChild->getRegister() == NULL
        && (firstChild->getOpCodeValue() == TR::iload || firstChild->getOpCodeValue() == TR::iloadi)
        && (firstChild->getNumChildren() > 0) && (firstChild->getFirstChild()->getNumChildren() == 1)
        && !(firstChild->getSymbolReference()->getSymbol()->isAtLeastOrStrongerThanAcquireRelease()
            && cg->comp()->target().isSMP())) {
        // Coming from memory, last use. Use flds to save the move
        TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(firstChild, 4, cg);
        TR::Register *tempReg = cg->allocateSinglePrecisionRegister();
        TR::Register *floatTrgReg = cg->allocateSinglePrecisionRegister();

        generateTrg1MemInstruction(cg, TR::InstOpCode::flds, firstChild, tempReg, tempMR);
        generateTrg1Src1Instruction(cg, opcode, node, floatTrgReg, tempReg);

        if (noFPRA) {
            if (resultCanStayInFloatRegister(cg->getCurrentEvaluationTreeTop()->getNode(), node)) {
                traceMsg(cg->comp(), "Result of node %p can stay in FP reg.\n", node);
                trgReg = floatTrgReg;
            } else {
                trgReg = moveFromFloatRegister(node, floatTrgReg, cg);
                cg->stopUsingRegister(floatTrgReg);
            }
        } else
            trgReg = floatTrgReg;

        cg->stopUsingRegister(tempReg);
        node->setRegister(trgReg);
        cg->decReferenceCount(firstChild);
    } else {
        // GPR -> FP -> Convert
        trgReg = singlePrecisionEvaluator(node, opcode, cg);
    }
    return trgReg;
}

TR::Register *OMR::ARM::TreeEvaluator::i2dEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // Supports iu2d, b2d, s2d, su2d
    TR::Node *firstChild = node->getFirstChild();
    TR::Register *src1Reg = NULL;
    TR::Register *trgReg = NULL;
    TR::Register *tempReg = cg->allocateSinglePrecisionRegister();

    TR::InstOpCode::Mnemonic opcode = (node->getOpCodeValue() != TR::iu2d && node->getOpCodeValue() != TR::su2d)
        ? TR::InstOpCode::fsitod
        : TR::InstOpCode::fuitod; // D[t], S[s]

    if (firstChild->getReferenceCount() == 1 && firstChild->getRegister() == NULL
        && (firstChild->getOpCodeValue() == TR::iload || firstChild->getOpCodeValue() == TR::iloadi)
        && (firstChild->getNumChildren() > 0) && (firstChild->getFirstChild()->getNumChildren() == 1)
        && !(firstChild->getSymbolReference()->getSymbol()->isAtLeastOrStrongerThanAcquireRelease()
            && cg->comp()->target().isSMP())) {
        // Coming from memory, last use
        TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(firstChild, 4, cg);

        generateTrg1MemInstruction(cg, TR::InstOpCode::flds, firstChild, tempReg, tempMR);
        tempMR->decNodeReferenceCounts();
    } else {
        // GPR -> FP -> Convert
        src1Reg = cg->evaluate(firstChild); // int
        tempReg = moveToFloatRegister(node, src1Reg, cg); // fmsr
        cg->decReferenceCount(firstChild);
    }

    TR::Register *doubleTrgReg = cg->allocateRegister(TR_FPR);
    generateTrg1Src1Instruction(cg, opcode, node, doubleTrgReg,
        tempReg); // The conversion.. source is float, result is double

    if (noFPRA) {
        if (resultCanStayInFloatRegister(cg->getCurrentEvaluationTreeTop()->getNode(), node)) {
            traceMsg(cg->comp(), "Result of node %p can stay in FP reg.\n", node);
            trgReg = doubleTrgReg;
        } else {
            trgReg = moveFromDoubleRegister(node, doubleTrgReg, cg);
            cg->stopUsingRegister(doubleTrgReg);
        }
    } else {
        trgReg = doubleTrgReg;
    }
    cg->stopUsingRegister(tempReg);
    node->setRegister(trgReg);
    return trgReg;
}

TR::Register *OMR::ARM::TreeEvaluator::l2fEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Register *trgReg = callLong2FloatHelper(node, cg);
    node->setRegister(trgReg);
    return trgReg;
}

TR::Register *OMR::ARM::TreeEvaluator::l2dEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Register *trgReg = callLong2DoubleHelper(node, cg);
    node->setRegister(trgReg);
    return trgReg;
}

// f2s handled by d2sEvaluator
// f2c handled by d2cEvaluator
// f2b handled by d2bEvaluator
// f2l handled by d2lEvaluator

TR::Register *OMR::ARM::TreeEvaluator::f2dEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Node *firstChild = node->getFirstChild();
    TR::Register *src1Reg = NULL;
    TR::Register *trgReg = NULL; // NEW
    TR::Register *doubleTrgReg = cg->allocateRegister(TR_FPR);

    if (firstChild->getReferenceCount() == 1 && firstChild->getRegister() == NULL
        && (firstChild->getOpCodeValue() == TR::fload || firstChild->getOpCodeValue() == TR::floadi)
        && (firstChild->getNumChildren() > 0) && (firstChild->getFirstChild()->getNumChildren() == 1)
        && !(firstChild->getSymbolReference()->getSymbol()->isAtLeastOrStrongerThanAcquireRelease()
            && cg->comp()->target().isSMP())) {
        // Coming from memory, last use
        TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(firstChild, 4, cg);
        TR::Register *tempReg = cg->allocateSinglePrecisionRegister();

        generateTrg1MemInstruction(cg, TR::InstOpCode::flds, firstChild, tempReg, tempMR);
        generateTrg1Src1Instruction(cg, TR::InstOpCode::fcvtds, node, doubleTrgReg, tempReg);

        tempMR->decNodeReferenceCounts();
        cg->stopUsingRegister(tempReg);
    } else {
        src1Reg = cg->evaluate(firstChild);
        generateTrg1Src1Instruction(cg, TR::InstOpCode::fcvtds, node, doubleTrgReg,
            moveToFloatRegister(node, src1Reg, cg));
        cg->decReferenceCount(firstChild);
    }

    if (noFPRA) {
        if (resultCanStayInFloatRegister(cg->getCurrentEvaluationTreeTop()->getNode(), node)) {
            traceMsg(cg->comp(), "Result of node %p can stay in FP reg.\n", node);
            trgReg = doubleTrgReg;
        } else {
            trgReg = moveFromDoubleRegister(node, doubleTrgReg, cg);
            cg->stopUsingRegister(doubleTrgReg);
        }
    } else {
        trgReg = doubleTrgReg;
    }

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

TR::Register *OMR::ARM::TreeEvaluator::f2iEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // Supports f2iu
    TR::Node *firstChild = node->getFirstChild();
    TR::Register *src1Reg = NULL;
    TR::Register *trgReg = NULL;
    TR::InstOpCode::Mnemonic opcode
        = (node->getOpCodeValue() == TR::f2i) ? TR::InstOpCode::ftosizs : TR::InstOpCode::ftouizs;
    TR::Register *floatTrgReg = cg->allocateSinglePrecisionRegister();

    if (firstChild->getReferenceCount() == 1 && firstChild->getRegister() == NULL
        && (firstChild->getOpCodeValue() == TR::fload || firstChild->getOpCodeValue() == TR::floadi)
        && (firstChild->getNumChildren() > 0) && (firstChild->getFirstChild()->getNumChildren() == 1)
        && !(firstChild->getSymbolReference()->getSymbol()->isAtLeastOrStrongerThanAcquireRelease()
            && cg->comp()->target().isSMP())) {
        // Coming from memory, last use
        TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(firstChild, 4, cg);
        TR::Register *tempReg = cg->allocateSinglePrecisionRegister();

        generateTrg1MemInstruction(cg, TR::InstOpCode::flds, firstChild, tempReg, tempMR);
        generateTrg1Src1Instruction(cg, opcode, node, floatTrgReg, tempReg);
        tempMR->decNodeReferenceCounts();
        cg->stopUsingRegister(tempReg);
    } else {
        src1Reg = cg->evaluate(firstChild);
        generateTrg1Src1Instruction(cg, opcode, node, floatTrgReg, moveToFloatRegister(node, src1Reg, cg));
        cg->decReferenceCount(firstChild);
    }

    // Integer result
    trgReg = moveFromFloatRegister(node, floatTrgReg, cg);
    cg->stopUsingRegister(floatTrgReg);

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

TR::Register *OMR::ARM::TreeEvaluator::d2iEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // Supports d2iu
    TR::Node *firstChild = node->getFirstChild();
    TR::Register *src1Reg = NULL;
    TR::Register *trgReg = NULL;
    TR::InstOpCode::Mnemonic opcode
        = (node->getOpCodeValue() == TR::d2i) ? TR::InstOpCode::ftosizd : TR::InstOpCode::ftouizd;
    TR::Register *floatTrgReg = cg->allocateSinglePrecisionRegister();

    if (firstChild->getReferenceCount() == 1 && firstChild->getRegister() == NULL
        && (firstChild->getOpCodeValue() == TR::dload || firstChild->getOpCodeValue() == TR::dloadi)
        && (firstChild->getNumChildren() > 0) && (firstChild->getFirstChild()->getNumChildren() == 1)
        && !(firstChild->getSymbolReference()->getSymbol()->isAtLeastOrStrongerThanAcquireRelease()
            && cg->comp()->target().isSMP())) {
        // Coming from memory, last use
        TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(firstChild, 4, cg);
        TR::Register *tempReg = cg->allocateRegister(TR_FPR); // double

        generateTrg1MemInstruction(cg, TR::InstOpCode::fldd, firstChild, tempReg, tempMR);
        generateTrg1Src1Instruction(cg, opcode, node, floatTrgReg, tempReg);
        tempMR->decNodeReferenceCounts();
        cg->stopUsingRegister(tempReg);
    } else {
        src1Reg = cg->evaluate(firstChild);
        generateTrg1Src1Instruction(cg, opcode, node, floatTrgReg, moveToDoubleRegister(node, src1Reg, cg));
        cg->decReferenceCount(firstChild);
    }

    // Integer result
    trgReg = moveFromFloatRegister(node, floatTrgReg, cg);
    cg->stopUsingRegister(floatTrgReg);

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

// also handles f2c
TR::Register *OMR::ARM::TreeEvaluator::d2cEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    diagnostic("Not expecting d2c/f2c\n");
    return NULL;
}

// also handles f2s
TR::Register *OMR::ARM::TreeEvaluator::d2sEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    diagnostic("Not expecting d2s/f2s\n");
    return NULL;
}

// also handles f2b
TR::Register *OMR::ARM::TreeEvaluator::d2bEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    diagnostic("Not expecting d2b/f2b\n");
    return NULL;
}

TR::Register *OMR::ARM::TreeEvaluator::f2lEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Register *trgReg = callFloat2LongHelper(node, cg);
    node->setRegister(trgReg);
    return trgReg;
}

TR::Register *OMR::ARM::TreeEvaluator::d2lEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Register *trgReg = callDouble2LongHelper(node, cg);
    node->setRegister(trgReg);
    return trgReg;
}

TR::Register *OMR::ARM::TreeEvaluator::d2fEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Node *firstChild = node->getFirstChild();
    TR::Register *src1Reg = NULL;
    TR::Register *trgReg = NULL;
    TR::Register *floatTrgReg = cg->allocateSinglePrecisionRegister(); // NEW

    if (firstChild->getReferenceCount() == 1 && firstChild->getRegister() == NULL
        && (firstChild->getOpCodeValue() == TR::dload || firstChild->getOpCodeValue() == TR::dloadi)
        && (firstChild->getNumChildren() > 0) && (firstChild->getFirstChild()->getNumChildren() == 1)
        && !(firstChild->getSymbolReference()->getSymbol()->isAtLeastOrStrongerThanAcquireRelease()
            && cg->comp()->target().isSMP())) {
        // Coming from memory, last use
        TR::Register *tempReg = cg->allocateRegister(TR_FPR);
        TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(firstChild, 8, cg);
        generateTrg1MemInstruction(cg, TR::InstOpCode::fldd, firstChild, tempReg, tempMR);
        generateTrg1Src1Instruction(cg, TR::InstOpCode::fcvtsd, node, floatTrgReg, tempReg);
        tempMR->decNodeReferenceCounts();
        cg->stopUsingRegister(tempReg);
    } else {
        src1Reg = cg->evaluate(firstChild);
        generateTrg1Src1Instruction(cg, TR::InstOpCode::fcvtsd, node, floatTrgReg,
            moveToDoubleRegister(node, src1Reg, cg));
        cg->decReferenceCount(firstChild);
    }

    if (noFPRA) {
        if (resultCanStayInFloatRegister(cg->getCurrentEvaluationTreeTop()->getNode(), node)) {
            traceMsg(cg->comp(), "Result of node %p can stay in FP reg.\n", node);
            trgReg = floatTrgReg;
        } else {
            trgReg = moveFromFloatRegister(node, floatTrgReg, cg);
            cg->stopUsingRegister(floatTrgReg);
        }
    } else {
        trgReg = floatTrgReg;
    }
    node->setRegister(trgReg);
    return trgReg;
}

static void ifFloatEvaluator(TR::Node *node, TR::InstOpCode::Mnemonic opCode, TR_ARMConditionCode cond,
    TR::CodeGenerator *cg, bool isDouble, bool unordered)
{
    TR::Node *firstChild = node->getFirstChild();
    TR::Node *secondChild = node->getSecondChild();
    TR::Register *src1Reg = cg->evaluate(firstChild);
    TR::Register *src2Reg = cg->evaluate(secondChild);
    TR::LabelSymbol *label = node->getBranchDestination()->getNode()->getLabel();

    TR::RegisterDependencyConditions *deps = NULL;

    if (noFPRA) {
        if (isDouble) {
            generateSrc2Instruction(cg, opCode, node, moveToDoubleRegister(node, src1Reg, cg),
                moveToDoubleRegister(node, src2Reg, cg));
        } else {
            generateSrc2Instruction(cg, opCode, node, moveToFloatRegister(node, src1Reg, cg),
                moveToFloatRegister(node, src2Reg, cg));
        }
    } else {
        generateSrc2Instruction(cg, opCode, node, src1Reg, src2Reg);
    }
    // Move the FPSCR flags back to the APSR
    generateInstruction(cg, TR::InstOpCode::fmstat, node);
    cg->decReferenceCount(firstChild);
    cg->decReferenceCount(secondChild);
    if (node->getNumChildren() == 3) {
        TR::Node *thirdChild = node->getChild(2);
        cg->evaluate(thirdChild);
        deps = generateRegisterDependencyConditions(cg, thirdChild, 0);
        cg->decReferenceCount(thirdChild);
    }

    if (deps) {
        generateConditionalBranchInstruction(cg, node, cond, label, deps);
        if (unordered)
            generateConditionalBranchInstruction(cg, node, ARMConditionCodeVS, label, deps);
    } else {
        generateConditionalBranchInstruction(cg, node, cond, label);
        if (unordered)
            generateConditionalBranchInstruction(cg, node, ARMConditionCodeVS, label);
    }
}

static TR::Register *setboolFloatEvaluator(TR::Node *node, TR::InstOpCode::Mnemonic opCode, TR_ARMConditionCode cond,
    TR::CodeGenerator *cg, bool isDouble, bool unordered)
{
    TR::Node *firstChild = node->getFirstChild();
    TR::Node *secondChild = node->getSecondChild();
    TR::Register *src1Reg = cg->evaluate(firstChild);
    TR::Register *src2Reg = cg->evaluate(secondChild);
    TR::Register *trgReg = cg->allocateRegister();

    if (noFPRA) {
        if (isDouble) {
            generateSrc2Instruction(cg, opCode, node, moveToDoubleRegister(node, src1Reg, cg),
                moveToDoubleRegister(node, src2Reg, cg));
        } else {
            generateSrc2Instruction(cg, opCode, node, moveToFloatRegister(node, src1Reg, cg),
                moveToFloatRegister(node, src2Reg, cg));
        }
    } else {
        generateSrc2Instruction(cg, opCode, node, src1Reg, src2Reg);
    }
    // Move the FPSCR flags back to the APSR
    generateInstruction(cg, TR::InstOpCode::fmstat, node);

    cg->decReferenceCount(firstChild);
    cg->decReferenceCount(secondChild);

    // Initialize the 0 result first
    generateTrg1ImmInstruction(cg, TR::InstOpCode::mov, node, trgReg, 0, 0);

    TR::Instruction *cursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::mov, node, trgReg, 1, 0);
    cursor->setConditionCode(cond);

    if (unordered) {
        cursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::mov, node, trgReg, 1, 0, cursor);
        cursor->setConditionCode(ARMConditionCodeVS);
    }
    node->setRegister(trgReg);
    return trgReg;
}

// also handles TR::iffcmpeq, TR::iffcmpequ, TR::ifdcmpequ
TR::Register *OMR::ARM::TreeEvaluator::ifdcmpeqEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    bool isDouble = (node->getOpCodeValue() == TR::ifdcmpeq || node->getOpCodeValue() == TR::ifdcmpequ);
    bool unordered = (node->getOpCodeValue() == TR::iffcmpequ || node->getOpCodeValue() == TR::ifdcmpequ);
    ifFloatEvaluator(node, isDouble ? TR::InstOpCode::fcmpd : TR::InstOpCode::fcmps, ARMConditionCodeEQ, cg, isDouble,
        unordered);
    return NULL;
}

// also handles TR::iffcmpne, TR::iffcmpneu, TR::ifdcmpneu
TR::Register *OMR::ARM::TreeEvaluator::ifdcmpneEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // ARMConditionCodeNE may imply unordered, but looks like it is not used in common code
    bool isDouble = (node->getOpCodeValue() == TR::ifdcmpne || node->getOpCodeValue() == TR::ifdcmpneu);

    ifFloatEvaluator(node, isDouble ? TR::InstOpCode::fcmpd : TR::InstOpCode::fcmps, ARMConditionCodeNE, cg, isDouble,
        false);
    return NULL;
}

// also handles TR::iffcmplt, TR::iffcmpltu, TR::ifdcmpltu
TR::Register *OMR::ARM::TreeEvaluator::ifdcmpltEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // ARMConditionCodeCC is for LT and ARMConditionCodeLT is for LTU
    bool isDouble = (node->getOpCodeValue() == TR::ifdcmplt || node->getOpCodeValue() == TR::ifdcmpltu);
    bool unordered = (node->getOpCodeValue() == TR::iffcmpltu || node->getOpCodeValue() == TR::ifdcmpltu);

    ifFloatEvaluator(node, isDouble ? TR::InstOpCode::fcmpd : TR::InstOpCode::fcmps,
        unordered ? ARMConditionCodeLT : ARMConditionCodeCC, cg, isDouble, false);
    return NULL;
}

// also handles TR::iffcmpge, TR::iffcmpgeu, TR::ifdcmpgeu
TR::Register *OMR::ARM::TreeEvaluator::ifdcmpgeEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // ARMConditionCodeGE is for GE and ARMConditionCodeCS is for GEU
    bool isDouble = (node->getOpCodeValue() == TR::ifdcmpge || node->getOpCodeValue() == TR::ifdcmpgeu);
    bool unordered = (node->getOpCodeValue() == TR::iffcmpgeu || node->getOpCodeValue() == TR::ifdcmpgeu);

    ifFloatEvaluator(node, isDouble ? TR::InstOpCode::fcmpd : TR::InstOpCode::fcmps,
        unordered ? ARMConditionCodeCS : ARMConditionCodeGE, cg, isDouble, false);
    return NULL;
}

// also handles TR::iffcmpgt, TR::iffcmpgtu, TR::ifdcmpgtu
TR::Register *OMR::ARM::TreeEvaluator::ifdcmpgtEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // ARMConditionCodeGT is for GT and ARMConditionCodeHI is for GTU
    bool isDouble = (node->getOpCodeValue() == TR::ifdcmpgt || node->getOpCodeValue() == TR::ifdcmpgtu);
    bool unordered = (node->getOpCodeValue() == TR::iffcmpgtu || node->getOpCodeValue() == TR::ifdcmpgtu);

    ifFloatEvaluator(node, isDouble ? TR::InstOpCode::fcmpd : TR::InstOpCode::fcmps,
        unordered ? ARMConditionCodeHI : ARMConditionCodeGT, cg, isDouble, false);
    return NULL;
}

// also handles TR::iffcmple, TR::iffcmpleu, TR::ifdcmpleu
TR::Register *OMR::ARM::TreeEvaluator::ifdcmpleEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // ARMConditionCodeLS is for LE and ARMConditionCodeLE is for LEU
    bool isDouble = (node->getOpCodeValue() == TR::ifdcmple || node->getOpCodeValue() == TR::ifdcmpleu);
    bool unordered = (node->getOpCodeValue() == TR::iffcmpleu || node->getOpCodeValue() == TR::ifdcmpleu);

    ifFloatEvaluator(node, isDouble ? TR::InstOpCode::fcmpd : TR::InstOpCode::fcmps,
        unordered ? ARMConditionCodeLE : ARMConditionCodeLS, cg, isDouble, false);
    return NULL;
}

// also handles TR::fcmpeq, TR::fcmpequ, TR::dcmpequ
TR::Register *OMR::ARM::TreeEvaluator::dcmpeqEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    bool isDouble = (node->getOpCodeValue() == TR::dcmpeq || node->getOpCodeValue() == TR::dcmpequ);
    bool unordered = (node->getOpCodeValue() == TR::fcmpequ || node->getOpCodeValue() == TR::dcmpequ);
    return setboolFloatEvaluator(node, isDouble ? TR::InstOpCode::fcmpd : TR::InstOpCode::fcmps, ARMConditionCodeEQ, cg,
        isDouble, unordered);
}

// also handles TR::fcmpne, TR::fcmpneu, TR::dcmpneu
TR::Register *OMR::ARM::TreeEvaluator::dcmpneEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // ARMConditionCodeNE may imply unordered, but looks like it is not used in common code
    bool isDouble = (node->getOpCodeValue() == TR::dcmpne || node->getOpCodeValue() == TR::dcmpneu);
    return setboolFloatEvaluator(node, isDouble ? TR::InstOpCode::fcmpd : TR::InstOpCode::fcmps, ARMConditionCodeNE, cg,
        isDouble, false);
}

// also handles TR::fcmplt, TR::fcmpltu, TR::dcmpltu
TR::Register *OMR::ARM::TreeEvaluator::dcmpltEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // ARMConditionCodeCC is for LT and ARMConditionCodeLT is for LTU
    bool isDouble = (node->getOpCodeValue() == TR::dcmplt || node->getOpCodeValue() == TR::dcmpltu);
    bool unordered = (node->getOpCodeValue() == TR::fcmpltu || node->getOpCodeValue() == TR::dcmpltu);
    return setboolFloatEvaluator(node, isDouble ? TR::InstOpCode::fcmpd : TR::InstOpCode::fcmps,
        unordered ? ARMConditionCodeLT : ARMConditionCodeCC, cg, isDouble, false);
}

// also handles TR::fcmpge
TR::Register *OMR::ARM::TreeEvaluator::dcmpgeEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // ARMConditionCodeGE is for GE and ARMConditionCodeCS is for GEU
    bool isDouble = (node->getOpCodeValue() == TR::dcmpge || node->getOpCodeValue() == TR::dcmpgeu);
    bool unordered = (node->getOpCodeValue() == TR::fcmpgeu || node->getOpCodeValue() == TR::dcmpgeu);

    return setboolFloatEvaluator(node, isDouble ? TR::InstOpCode::fcmpd : TR::InstOpCode::fcmps,
        unordered ? ARMConditionCodeCS : ARMConditionCodeGE, cg, isDouble, false);
}

TR::Register *OMR::ARM::TreeEvaluator::dcmpgtEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // ARMConditionCodeGT is for GT and ARMConditionCodeHI is for GTU
    bool isDouble = (node->getOpCodeValue() == TR::dcmpgt || node->getOpCodeValue() == TR::dcmpgtu);
    bool unordered = (node->getOpCodeValue() == TR::fcmpgtu || node->getOpCodeValue() == TR::dcmpgtu);

    return setboolFloatEvaluator(node, isDouble ? TR::InstOpCode::fcmpd : TR::InstOpCode::fcmps,
        unordered ? ARMConditionCodeHI : ARMConditionCodeGT, cg, isDouble, false);
}

TR::Register *OMR::ARM::TreeEvaluator::dcmpleEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // ARMConditionCodeLS is for LE and ARMConditionCodeLE is for LEU
    bool isDouble = (node->getOpCodeValue() == TR::dcmple || node->getOpCodeValue() == TR::dcmpleu);
    bool unordered = (node->getOpCodeValue() == TR::fcmpleu || node->getOpCodeValue() == TR::dcmpleu);

    return setboolFloatEvaluator(node, isDouble ? TR::InstOpCode::fcmpd : TR::InstOpCode::fcmps,
        unordered ? ARMConditionCodeLE : ARMConditionCodeLS, cg, isDouble, false);
}

TR::Register *OMR::ARM::TreeEvaluator::dcmplEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // Support TR::fcmpl, TR::dcmpg and TR::fcmpg as well
    // TR::dcmpl/TR::fcmpl (1 if c1 > c2, 0 if c1 == c2, -1 if c1 < c2 or unordered)
    // TR::dcmpg/TR::fcmpg (1 if c1 > c2 or unordered, 0 if c1 == c2, -1 if c1 < c2)
    TR::Node *firstChild = node->getFirstChild();
    TR::Node *secondChild = node->getSecondChild();
    TR::Register *src1Reg = cg->evaluate(firstChild);
    TR::Register *src2Reg = cg->evaluate(secondChild);
    TR::Register *trgReg = cg->allocateRegister();
    bool isDouble = (node->getOpCodeValue() == TR::dcmpg || node->getOpCodeValue() == TR::dcmpl);
    bool isGType = (node->getOpCodeValue() == TR::dcmpg || node->getOpCodeValue() == TR::fcmpg);

    if (noFPRA) {
        if (isDouble) {
            generateSrc2Instruction(cg, isDouble ? TR::InstOpCode::fcmpd : TR::InstOpCode::fcmps, node,
                moveToDoubleRegister(node, src1Reg, cg), moveToDoubleRegister(node, src2Reg, cg));
        } else {
            generateSrc2Instruction(cg, isDouble ? TR::InstOpCode::fcmpd : TR::InstOpCode::fcmps, node,
                moveToFloatRegister(node, src1Reg, cg), moveToFloatRegister(node, src2Reg, cg));
        }
    } else {
        generateSrc2Instruction(cg, isDouble ? TR::InstOpCode::fcmpd : TR::InstOpCode::fcmps, node, src1Reg, src2Reg);
    }
    // Move the FPSCR flags back to the APSR
    generateInstruction(cg, TR::InstOpCode::fmstat, node);

    cg->decReferenceCount(firstChild);
    cg->decReferenceCount(secondChild);

    TR::Instruction *cursor = generateTrg1ImmInstruction(cg, TR::InstOpCode::mov, node, trgReg, 0, 0);
    cursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::add, node, trgReg, trgReg, 1, 0, cursor);
    if (isGType)
        cursor->setConditionCode(ARMConditionCodeHI); // GT or unordered
    else
        cursor->setConditionCode(ARMConditionCodeGT); // GT

    cursor = generateTrg1Src1ImmInstruction(cg, TR::InstOpCode::sub, node, trgReg, trgReg, 1, 0, cursor);
    if (isGType)
        cursor->setConditionCode(ARMConditionCodeCC); // LT
    else
        cursor->setConditionCode(ARMConditionCodeLT); // LT or unordered

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

TR::Register *OMR::ARM::TreeEvaluator::fRegLoadEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // Support dRegLoad
    TR::Register *globalReg = node->getRegister();
    if (globalReg == NULL) {
        globalReg = cg->allocateRegister(TR_FPR);
        if (node->getOpCodeValue() == TR::fRegLoad)
            globalReg->setIsSinglePrecision();
        node->setRegister(globalReg);
    }
    return globalReg;
}

TR::Register *OMR::ARM::TreeEvaluator::fRegStoreEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // Support dRegStore
    TR::Node *child = node->getFirstChild();
    TR::Register *globalReg = cg->evaluate(child);
    cg->decReferenceCount(child);
    return globalReg;
}

static TR::Register *generateFloatMaxMin(TR::Node *node, TR::CodeGenerator *cg, bool max)
{
    TR::Node *firstChild = node->getFirstChild();
    TR::DataType data_type = firstChild->getDataType();
    TR::DataType type = firstChild->getType();
    TR::Instruction *cursor;
    TR::Register *trgReg, *trgFloatReg;

    if (data_type != TR::Float && data_type != TR::Double && node->getNumChildren() == 2) {
        TR_ASSERT(false, "assertion failure"); // We shouldn't get here at all
    }

    bool isFloat = (data_type == TR::Float);
    TR::Register *reg = cg->evaluate(firstChild);
    TR::LabelSymbol *doneLabel = TR::LabelSymbol::create(cg->trHeapMemory(), cg);

    if (noFPRA && reg->getKind() == TR_GPR) {
        trgFloatReg = isFloat ? moveToFloatRegister(node, reg, cg) : moveToDoubleRegister(node, reg, cg);
        trgReg = reg;
    } else {
        if (cg->canClobberNodesRegister(firstChild)) {
            trgFloatReg = reg; // use first child as a target
        } else {
            if (isFloat) {
                trgFloatReg = cg->allocateSinglePrecisionRegister();
                generateTrg1Src1Instruction(cg, TR::InstOpCode::fcpys, node, trgFloatReg,
                    moveToFloatRegister(node, reg, cg));
            } else {
                trgFloatReg = cg->allocateRegister(TR_FPR);
                generateTrg1Src1Instruction(cg, TR::InstOpCode::fcpyd, node, trgFloatReg,
                    moveToDoubleRegister(node, reg, cg));
            }
        }
        if (max) {
            trgReg = isFloat ? moveFromFloatRegister(node, reg, cg) : moveFromDoubleRegister(node, reg, cg);
        }
    }

    TR::Node *child = node->getChild(1); // Second child
    TR::Register *childReg = cg->evaluate(child);
    TR::Register *childFloatReg;

    if (noFPRA && childReg->getKind() == TR_GPR) {
        childFloatReg = (isFloat) ? moveToFloatRegister(node, childReg, cg) : moveToDoubleRegister(node, childReg, cg);
    } else {
        childFloatReg = childReg;
        if (!max) {
            childReg = (isFloat) ? moveFromFloatRegister(node, childFloatReg, cg)
                                 : moveFromDoubleRegister(node, childFloatReg, cg);
        }
    }

    TR::RegisterDependencyConditions *deps
        = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, 1, cg->trMemory());
    deps->addPostCondition(trgFloatReg, TR::RealRegister::NoReg);

    // Check for NaN by comparing itself and leave
    generateSrc2Instruction(cg, (isFloat) ? TR::InstOpCode::fcmps : TR::InstOpCode::fcmpd, node, trgFloatReg,
        trgFloatReg);
    generateInstruction(cg, TR::InstOpCode::fmstat, node);
    generateConditionalBranchInstruction(cg, node, ARMConditionCodeVS, doneLabel, deps);

    // Check for NaN
    cursor = generateSrc2Instruction(cg, (isFloat) ? TR::InstOpCode::fcmps : TR::InstOpCode::fcmpd, node, childFloatReg,
        childFloatReg);
    cursor = generateInstruction(cg, TR::InstOpCode::fmstat, node, cursor);
    cursor = generateTrg1Src1Instruction(cg, (isFloat) ? TR::InstOpCode::fcpys : TR::InstOpCode::fcpyd, node,
        trgFloatReg, childFloatReg, cursor);
    cursor->setConditionCode(ARMConditionCodeVS);
    cursor = generateConditionalBranchInstruction(cg, node, ARMConditionCodeVS, doneLabel, deps, cursor);

    // TODO: Special case firstChild is -0.0, secondChild is 0.0, compare it in general register
    if (isFloat) {
        cursor = generateSrc1ImmInstruction(cg, TR::InstOpCode::cmp, node, max ? trgReg : childReg, 0x80, 24,
            cursor); // 0x80000000
    } else {
        cursor = generateSrc1ImmInstruction(cg, TR::InstOpCode::cmp, node,
            max ? trgReg->getHighOrder() : childReg->getHighOrder(), 0x80, 24, cursor); // 0x80000000
        cursor = generateSrc1ImmInstruction(cg, TR::InstOpCode::cmp, node,
            max ? trgReg->getLowOrder() : childReg->getLowOrder(), 0, 0, cursor);
        cursor->setConditionCode(ARMConditionCodeEQ);
    }

    /*
     * ;max:                                       ;min:
     * ;compare child with zero if target is -0.0  ;compare target with zero if child is -0.0
     * fcmpzdeq child                              fcmpzdeq target
     * fmstateq                                    fmstateq
     * ;if child is zero, then return child (0.0). ;if target is zero, then return child (-0.0).
     * fcpydeq  target child                       fcpydeq  target child
     * ;if child is not zero, return greater one.  ;if target is not zero, return lesser one.
     * fcmpdne  child  target                      fcmpdne  child  target
     * fmstatne                                    fmstatne
     * fcpydhi  target child                       fcpydlt  target child
     *
     */
    cursor = generateSrc1ImmInstruction(cg, (isFloat) ? TR::InstOpCode::fcmpzs : TR::InstOpCode::fcmpzd, node,
        max ? childFloatReg : trgFloatReg, 0, 0, cursor); // Dummy 0
    cursor->setConditionCode(ARMConditionCodeEQ);
    cursor = generateInstruction(cg, TR::InstOpCode::fmstat, node, cursor);
    cursor->setConditionCode(ARMConditionCodeEQ);
    cursor = generateTrg1Src1Instruction(cg, (isFloat) ? TR::InstOpCode::fcpys : TR::InstOpCode::fcpyd, node,
        trgFloatReg, childFloatReg, cursor);
    cursor->setConditionCode(ARMConditionCodeEQ);
    cursor = generateSrc2Instruction(cg, (isFloat) ? TR::InstOpCode::fcmps : TR::InstOpCode::fcmpd, node, childFloatReg,
        trgFloatReg, cursor);
    cursor->setConditionCode(ARMConditionCodeNE);
    // Move the FPSCR flags back to the APSR
    cursor = generateInstruction(cg, TR::InstOpCode::fmstat, node, cursor);
    cursor->setConditionCode(ARMConditionCodeNE);
    cursor = generateTrg1Src1Instruction(cg, (isFloat) ? TR::InstOpCode::fcpys : TR::InstOpCode::fcpyd, node,
        trgFloatReg, childFloatReg, cursor);
    cursor->setConditionCode(max ? ARMConditionCodeHI : ARMConditionCodeLT);

    // doneLabel
    cursor = generateLabelInstruction(cg, TR::InstOpCode::label, node, doneLabel, deps, cursor);

    if (noFPRA && reg->getKind() == TR_GPR) {
        if (cg->canClobberNodesRegister(firstChild)) {
            if (isFloat) {
                cursor = generateTrg1Src1Instruction(cg, TR::InstOpCode::fmrs, node, reg, trgFloatReg, cursor);
            } else {
                cursor = generateTrg2Src1Instruction(cg, TR::InstOpCode::fmrrd, node, reg->getLowOrder(),
                    reg->getHighOrder(), trgFloatReg, cursor);
            }
            trgReg = reg;
        } else {
            trgReg = (isFloat) ? moveFromFloatRegister(node, trgFloatReg, cg)
                               : moveFromDoubleRegister(node, trgFloatReg, cg);
            cg->stopUsingRegister(trgFloatReg);
        }
    } else {
        trgReg = trgFloatReg;
    }

    node->setRegister(trgReg);
    for (int i = 0; i < 2; i++) // n = 2
    {
        cg->decReferenceCount(node->getChild(i));
    }
    return trgReg;
}

TR::Register *OMR::ARM::TreeEvaluator::fmaxEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    return generateFloatMaxMin(node, cg, true);
}

TR::Register *OMR::ARM::TreeEvaluator::fminEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    return generateFloatMaxMin(node, cg, false);
}

#else
TR::Register *OMR::ARM::TreeEvaluator::ibits2fEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Node *child = node->getFirstChild();
    TR::Register *target = cg->allocateRegister();

    if (child->getRegister() == NULL && child->getReferenceCount() == 1 && child->getOpCode().isLoadVar()) {
        TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(child, 4, cg);
        generateTrg1MemInstruction(cg, TR::InstOpCode::ldr, node, target, tempMR);
        tempMR->decNodeReferenceCounts();
    } else {
        target = cg->evaluate(child);
        cg->decReferenceCount(child);
    }
    node->setRegister(target);
    return target;
}

TR::Register *OMR::ARM::TreeEvaluator::fbits2iEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Node *child = node->getFirstChild();
    TR::Register *target = cg->allocateRegister();

    TR_ASSERT(!node->normalizeNanValues(), "Check NAN\n");
    if (child->getRegister() == NULL && child->getReferenceCount() == 1 && child->getOpCode().isLoadVar()) {
        TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(child, 4, cg);
        generateTrg1MemInstruction(cg, TR::InstOpCode::ldr, node, target, tempMR);
        tempMR->decNodeReferenceCounts();
    } else {
        target = cg->evaluate(child);
        cg->decReferenceCount(child);
    }
    node->setRegister(target);
    return target;
}

TR::Register *OMR::ARM::TreeEvaluator::lbits2dEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Node *child = node->getFirstChild();
    TR::RegisterPair *pairTarget = NULL;
    TR::Register *target = NULL;
    TR::Register *lowReg = NULL;
    TR::Register *highReg = NULL;
    TR::Compilation *comp = cg->comp();

    if (child->getRegister() == NULL && child->getReferenceCount() == 1 && child->getOpCode().isLoadVar()) {
        TR::MemoryReference *highMem, *lowMem;
        TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(child, 8, cg);
        lowReg = cg->allocateRegister();
        highReg = cg->allocateRegister();
        highMem = new (cg->trHeapMemory())
            TR::MemoryReference(*tempMR, (cg->comp()->target().cpu.isBigEndian()) ? 0 : 4, 4, cg);
        generateTrg1MemInstruction(cg, TR::InstOpCode::ldr, node, highReg, highMem);
        lowMem = new (cg->trHeapMemory())
            TR::MemoryReference(*tempMR, (cg->comp()->target().cpu.isBigEndian()) ? 4 : 0, 4, cg);
        generateTrg1MemInstruction(cg, TR::InstOpCode::ldr, node, lowReg, lowMem);

        highMem->decNodeReferenceCounts();
        lowMem->decNodeReferenceCounts();
        pairTarget = cg->allocateRegisterPair(lowReg, highReg);
        node->setRegister(pairTarget);
        return pairTarget;
    } else {
        target = cg->evaluate(child);
        cg->decReferenceCount(child);
        node->setRegister(target);
        return target;
    }
}

TR::Register *OMR::ARM::TreeEvaluator::dbits2lEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    TR::Node *child = node->getFirstChild();
    TR::RegisterPair *pairTarget = NULL;
    TR::Register *target = NULL;
    TR::Register *lowReg = NULL;
    TR::Register *highReg = NULL;
    TR::Compilation *comp = cg->comp();

    TR_ASSERT(!node->normalizeNanValues(), "Check NAN\n");
    if (child->getRegister() == NULL && child->getReferenceCount() == 1 && child->getOpCode().isLoadVar()) {
        TR::MemoryReference *highMem, *lowMem;
        TR::MemoryReference *tempMR = new (cg->trHeapMemory()) TR::MemoryReference(child, 8, cg);
        lowReg = cg->allocateRegister();
        highReg = cg->allocateRegister();
        highMem = new (cg->trHeapMemory())
            TR::MemoryReference(*tempMR, (cg->comp()->target().cpu.isBigEndian()) ? 0 : 4, 4, cg);
        generateTrg1MemInstruction(cg, TR::InstOpCode::ldr, node, highReg, highMem);
        lowMem = new (cg->trHeapMemory())
            TR::MemoryReference(*tempMR, (cg->comp()->target().cpu.isBigEndian()) ? 4 : 0, 4, cg);
        generateTrg1MemInstruction(cg, TR::InstOpCode::ldr, node, lowReg, lowMem);

        highMem->decNodeReferenceCounts();
        lowMem->decNodeReferenceCounts();
        pairTarget = cg->allocateRegisterPair(lowReg, highReg);
        node->setRegister(pairTarget);
        return pairTarget;
    } else {
        target = cg->evaluate(child);
        cg->decReferenceCount(child);
        node->setRegister(target);
        return target;
    }
}

TR::Register *OMR::ARM::TreeEvaluator::fconstEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::dconstEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

// also handles TR::floadi
TR::Register *OMR::ARM::TreeEvaluator::floadEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

// also handles TR::dloadi
TR::Register *OMR::ARM::TreeEvaluator::dloadEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::fstoreEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::dstoreEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::fstoreiEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::dstoreiEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::freturnEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::dreturnEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::faddEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::daddEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::dsubEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::fsubEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::fmulEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::dmulEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::fdivEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::ddivEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::fremEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::dremEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::fabsEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::dabsEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::fnegEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::dnegEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::i2fEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::i2dEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::l2fEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::l2dEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

// f2i handled by d2iEvaluator
// f2s handled by d2sEvaluator
// f2c handled by d2cEvaluator
// f2b handled by d2bEvaluator
// f2l handled by d2lEvaluator

TR::Register *OMR::ARM::TreeEvaluator::f2dEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::f2iEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::d2iEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

// also handles f2c
TR::Register *OMR::ARM::TreeEvaluator::d2cEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

// also handles f2s
TR::Register *OMR::ARM::TreeEvaluator::d2sEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

// also handles f2b
TR::Register *OMR::ARM::TreeEvaluator::d2bEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::f2lEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::d2lEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::d2fEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::b2dEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

// also handles s2f
TR::Register *OMR::ARM::TreeEvaluator::s2dEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

// also handles c2f
TR::Register *OMR::ARM::TreeEvaluator::su2dEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

// also handles TR::iffcmpeq
TR::Register *OMR::ARM::TreeEvaluator::ifdcmpeqEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

// also handles TR::iffcmpeq
TR::Register *OMR::ARM::TreeEvaluator::ifdcmpneEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

// also handles TR::iffcmpeq
TR::Register *OMR::ARM::TreeEvaluator::ifdcmpltEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

// also handles TR::iffcmpeq
TR::Register *OMR::ARM::TreeEvaluator::ifdcmpgeEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

// also handles TR::iffcmpgt
TR::Register *OMR::ARM::TreeEvaluator::ifdcmpgtEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

// also handles TR::iffcmple
TR::Register *OMR::ARM::TreeEvaluator::ifdcmpleEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

// also handles TR::iffcmpequ
TR::Register *OMR::ARM::TreeEvaluator::ifdcmpequEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

// also handles TR::iffcmpltu
TR::Register *OMR::ARM::TreeEvaluator::ifdcmpltuEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

// also handles TR::iffcmpgeu
TR::Register *OMR::ARM::TreeEvaluator::ifdcmpgeuEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // ARMOp_bge is really not less than which is equivalent to greater than equal to or unordered
    return NULL;
}

// also handles TR::iffcmpgtu
TR::Register *OMR::ARM::TreeEvaluator::ifdcmpgtuEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

// also handles TR::iffcmpleu
TR::Register *OMR::ARM::TreeEvaluator::ifdcmpleuEvaluator(TR::Node *node, TR::CodeGenerator *cg)
{
    // ARMOp_ble is really not greater than which is equivalent to less than equal to or unordered
    return NULL;
}

// also handles TR::fcmpeq
TR::Register *OMR::ARM::TreeEvaluator::dcmpeqEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

// also handles TR::fcmpne
TR::Register *OMR::ARM::TreeEvaluator::dcmpneEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

// also handles TR::fcmplt
TR::Register *OMR::ARM::TreeEvaluator::dcmpltEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

// also handles TR::fcmpge
TR::Register *OMR::ARM::TreeEvaluator::dcmpgeEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::dcmpgtEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::dcmpleEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::dcmpequEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::dcmpltuEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::dcmpgeuEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::dcmpgtuEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::dcmpleuEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::dcmplEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::dcmpgEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::fRegLoadEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::fRegStoreEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::fmaxEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

TR::Register *OMR::ARM::TreeEvaluator::fminEvaluator(TR::Node *node, TR::CodeGenerator *cg) { return NULL; }

#endif
