summaryrefslogtreecommitdiff
path: root/src/jit/registerfp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/jit/registerfp.cpp')
-rw-r--r--src/jit/registerfp.cpp1522
1 files changed, 1522 insertions, 0 deletions
diff --git a/src/jit/registerfp.cpp b/src/jit/registerfp.cpp
new file mode 100644
index 0000000000..997c223ed4
--- /dev/null
+++ b/src/jit/registerfp.cpp
@@ -0,0 +1,1522 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "jitpch.h"
+#ifdef _MSC_VER
+#pragma hdrstop
+#endif
+
+#ifdef LEGACY_BACKEND // This file is NOT used for the RyuJIT backend that uses the linear scan register allocator.
+
+#include "compiler.h"
+#include "emit.h"
+#include "codegen.h"
+
+#ifndef _TARGET_ARM_
+#error "Non-ARM target for registerfp.cpp"
+#endif // !_TARGET_ARM_
+
+// get the next argument register which is aligned to 'alignment' # of bytes
+regNumber alignFloatArgReg(regNumber argReg, int alignment)
+{
+ assert(isValidFloatArgReg(argReg));
+
+ int regsize_alignment = alignment /= REGSIZE_BYTES;
+ if (genMapFloatRegNumToRegArgNum(argReg) % regsize_alignment)
+ argReg = genRegArgNext(argReg);
+
+ // technically the above should be a 'while' so make sure
+ // we never should have incremented more than once
+ assert(!(genMapFloatRegNumToRegArgNum(argReg) % regsize_alignment));
+
+ return argReg;
+}
+
+// Instruction list
+// N=normal, R=reverse, P=pop
+
+void CodeGen::genFloatConst(GenTree* tree, RegSet::RegisterPreference* pref)
+{
+ assert(tree->gtOper == GT_CNS_DBL);
+ var_types type = tree->gtType;
+ double constValue = tree->gtDblCon.gtDconVal;
+ size_t* cv = (size_t*)&constValue;
+
+ regNumber dst = regSet.PickRegFloat(type, pref);
+
+ if (type == TYP_FLOAT)
+ {
+ regNumber reg = regSet.rsPickReg();
+
+ float f = forceCastToFloat(constValue);
+ genSetRegToIcon(reg, *((int*)(&f)));
+ getEmitter()->emitIns_R_R(INS_vmov_i2f, EA_4BYTE, dst, reg);
+ }
+ else
+ {
+ assert(type == TYP_DOUBLE);
+ regNumber reg1 = regSet.rsPickReg();
+ regNumber reg2 = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(reg1));
+
+ genSetRegToIcon(reg1, cv[0]);
+ regSet.rsLockReg(genRegMask(reg1));
+ genSetRegToIcon(reg2, cv[1]);
+ regSet.rsUnlockReg(genRegMask(reg1));
+
+ getEmitter()->emitIns_R_R_R(INS_vmov_i2d, EA_8BYTE, dst, reg1, reg2);
+ }
+ genMarkTreeInReg(tree, dst);
+
+ return;
+}
+
+void CodeGen::genFloatMath(GenTree* tree, RegSet::RegisterPreference* pref)
+{
+ assert(tree->OperGet() == GT_INTRINSIC);
+
+ GenTreePtr op1 = tree->gtOp.gtOp1;
+
+ // get tree into a register
+ genCodeForTreeFloat(op1, pref);
+
+ instruction ins;
+
+ switch (tree->gtIntrinsic.gtIntrinsicId)
+ {
+ case CORINFO_INTRINSIC_Sin:
+ ins = INS_invalid;
+ break;
+ case CORINFO_INTRINSIC_Cos:
+ ins = INS_invalid;
+ break;
+ case CORINFO_INTRINSIC_Sqrt:
+ ins = INS_vsqrt;
+ break;
+ case CORINFO_INTRINSIC_Abs:
+ ins = INS_vabs;
+ break;
+ case CORINFO_INTRINSIC_Round:
+ {
+ regNumber reg = regSet.PickRegFloat(tree->TypeGet(), pref);
+ genMarkTreeInReg(tree, reg);
+ // convert it to a long and back
+ inst_RV_RV(ins_FloatConv(TYP_LONG, tree->TypeGet()), reg, op1->gtRegNum, tree->TypeGet());
+ inst_RV_RV(ins_FloatConv(tree->TypeGet(), TYP_LONG), reg, reg);
+ genCodeForTreeFloat_DONE(tree, op1->gtRegNum);
+ return;
+ }
+ break;
+ default:
+ unreached();
+ }
+
+ if (ins != INS_invalid)
+ {
+ regNumber reg = regSet.PickRegFloat(tree->TypeGet(), pref);
+ genMarkTreeInReg(tree, reg);
+ inst_RV_RV(ins, reg, op1->gtRegNum, tree->TypeGet());
+ // mark register that holds tree
+ genCodeForTreeFloat_DONE(tree, reg);
+ }
+ else
+ {
+ unreached();
+ // If unreached is removed, mark register that holds tree
+ // genCodeForTreeFloat_DONE(tree, op1->gtRegNum);
+ }
+
+ return;
+}
+
+void CodeGen::genFloatSimple(GenTree* tree, RegSet::RegisterPreference* pref)
+{
+ assert(tree->OperKind() & GTK_SMPOP);
+ var_types type = tree->TypeGet();
+
+ RegSet::RegisterPreference defaultPref(RBM_ALLFLOAT, RBM_NONE);
+ if (pref == NULL)
+ {
+ pref = &defaultPref;
+ }
+
+ switch (tree->OperGet())
+ {
+ // Assignment
+ case GT_ASG:
+ {
+ genFloatAssign(tree);
+ break;
+ }
+
+ // Arithmetic binops
+ case GT_ADD:
+ case GT_SUB:
+ case GT_MUL:
+ case GT_DIV:
+ {
+ genFloatArith(tree, pref);
+ break;
+ }
+
+ case GT_NEG:
+ {
+ GenTreePtr op1 = tree->gtOp.gtOp1;
+
+ // get the tree into a register
+ genCodeForTreeFloat(op1, pref);
+
+ // change the sign
+ regNumber reg = regSet.PickRegFloat(type, pref);
+ genMarkTreeInReg(tree, reg);
+ inst_RV_RV(ins_MathOp(tree->OperGet(), type), reg, op1->gtRegNum, type);
+
+ // mark register that holds tree
+ genCodeForTreeFloat_DONE(tree, reg);
+ return;
+ }
+
+ case GT_IND:
+ {
+ regMaskTP addrReg;
+
+ // Make sure the address value is 'addressable' */
+ addrReg = genMakeAddressable(tree, 0, RegSet::FREE_REG);
+
+ // Load the value onto the FP stack
+ regNumber reg = regSet.PickRegFloat(type, pref);
+ genLoadFloat(tree, reg);
+
+ genDoneAddressable(tree, addrReg, RegSet::FREE_REG);
+
+ genCodeForTreeFloat_DONE(tree, reg);
+
+ break;
+ }
+ case GT_CAST:
+ {
+ genCodeForTreeCastFloat(tree, pref);
+ break;
+ }
+
+ // Asg-Arithmetic ops
+ case GT_ASG_ADD:
+ case GT_ASG_SUB:
+ case GT_ASG_MUL:
+ case GT_ASG_DIV:
+ {
+ genFloatAsgArith(tree);
+ break;
+ }
+ case GT_INTRINSIC:
+ genFloatMath(tree, pref);
+ break;
+
+ case GT_RETURN:
+ {
+ GenTreePtr op1 = tree->gtOp.gtOp1;
+ assert(op1);
+
+ pref->best = (type == TYP_DOUBLE) ? RBM_DOUBLERET : RBM_FLOATRET;
+
+ // Compute the result
+ genCodeForTreeFloat(op1, pref);
+
+ inst_RV_TT(ins_FloatConv(tree->TypeGet(), op1->TypeGet()), REG_FLOATRET, op1);
+ if (compiler->info.compIsVarArgs || compiler->opts.compUseSoftFP)
+ {
+ if (tree->TypeGet() == TYP_FLOAT)
+ {
+ inst_RV_RV(INS_vmov_f2i, REG_INTRET, REG_FLOATRET, TYP_FLOAT, EA_4BYTE);
+ }
+ else
+ {
+ assert(tree->TypeGet() == TYP_DOUBLE);
+ inst_RV_RV_RV(INS_vmov_d2i, REG_INTRET, REG_NEXT(REG_INTRET), REG_FLOATRET, EA_8BYTE);
+ }
+ }
+ break;
+ }
+ case GT_ARGPLACE:
+ break;
+
+ case GT_COMMA:
+ {
+ GenTreePtr op1 = tree->gtOp.gtOp1;
+ GenTreePtr op2 = tree->gtGetOp2();
+
+ if (tree->gtFlags & GTF_REVERSE_OPS)
+ {
+ genCodeForTreeFloat(op2, pref);
+
+ regSet.SetUsedRegFloat(op2, true);
+ genEvalSideEffects(op1);
+ regSet.SetUsedRegFloat(op2, false);
+ }
+ else
+ {
+ genEvalSideEffects(op1);
+ genCodeForTreeFloat(op2, pref);
+ }
+
+ genCodeForTreeFloat_DONE(tree, op2->gtRegNum);
+ break;
+ }
+
+ case GT_CKFINITE:
+ genFloatCheckFinite(tree, pref);
+ break;
+
+ default:
+ NYI("Unhandled register FP codegen");
+ }
+}
+
+// generate code for ckfinite tree/instruction
+void CodeGen::genFloatCheckFinite(GenTree* tree, RegSet::RegisterPreference* pref)
+{
+ TempDsc* temp;
+ int offs;
+
+ GenTreePtr op1 = tree->gtOp.gtOp1;
+
+ // Offset of the DWord containing the exponent
+ offs = (op1->gtType == TYP_FLOAT) ? 0 : sizeof(int);
+
+ // get tree into a register
+ genCodeForTreeFloat(op1, pref);
+
+ regNumber reg = regSet.rsPickReg();
+
+ int expMask;
+ if (op1->gtType == TYP_FLOAT)
+ {
+ getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, reg, op1->gtRegNum);
+ expMask = 0x7F800000;
+ }
+ else // double
+ {
+ assert(op1->gtType == TYP_DOUBLE);
+ getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, reg,
+ REG_NEXT(op1->gtRegNum)); // the high 32 bits of the double register
+ expMask = 0x7FF00000;
+ }
+ regTracker.rsTrackRegTrash(reg);
+
+ // Check if the exponent is all ones
+ inst_RV_IV(INS_and, reg, expMask, EA_4BYTE);
+ inst_RV_IV(INS_cmp, reg, expMask, EA_4BYTE);
+
+ // If exponent was all 1's, we need to throw ArithExcep
+ emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED);
+ genJumpToThrowHlpBlk(jmpEqual, SCK_ARITH_EXCPN);
+
+ genCodeForTreeFloat_DONE(tree, op1->gtRegNum);
+}
+
+void CodeGen::genFloatAssign(GenTree* tree)
+{
+ var_types type = tree->TypeGet();
+ GenTreePtr op1 = tree->gtGetOp1();
+ GenTreePtr op2 = tree->gtGetOp2();
+
+ regMaskTP needRegOp1 = RBM_ALLINT;
+ regMaskTP addrReg = RBM_NONE;
+ bool volat = false; // Is this a volatile store
+ bool unaligned = false; // Is this an unaligned store
+ regNumber op2reg = REG_NA;
+
+#ifdef DEBUGGING_SUPPORT
+ unsigned lclVarNum = compiler->lvaCount;
+ unsigned lclILoffs = DUMMY_INIT(0);
+#endif
+
+ noway_assert(tree->OperGet() == GT_ASG);
+
+ // Is the target a floating-point local variable?
+ // possibly even an enregistered floating-point local variable?
+ //
+ switch (op1->gtOper)
+ {
+ unsigned varNum;
+ LclVarDsc* varDsc;
+
+ case GT_LCL_FLD:
+ // Check for a misalignment on a Floating Point field
+ //
+ if (varTypeIsFloating(op1->TypeGet()))
+ {
+ if ((op1->gtLclFld.gtLclOffs % emitTypeSize(op1->TypeGet())) != 0)
+ {
+ unaligned = true;
+ }
+ }
+ break;
+
+ case GT_LCL_VAR:
+ varNum = op1->gtLclVarCommon.gtLclNum;
+ noway_assert(varNum < compiler->lvaCount);
+ varDsc = compiler->lvaTable + varNum;
+
+#ifdef DEBUGGING_SUPPORT
+ // For non-debuggable code, every definition of a lcl-var has
+ // to be checked to see if we need to open a new scope for it.
+ // Remember the local var info to call siCheckVarScope
+ // AFTER code generation of the assignment.
+ //
+ if (compiler->opts.compScopeInfo && !compiler->opts.compDbgCode && (compiler->info.compVarScopesCount > 0))
+ {
+ lclVarNum = varNum;
+ lclILoffs = op1->gtLclVar.gtLclILoffs;
+ }
+#endif
+
+ // Dead Store assert (with min opts we may have dead stores)
+ //
+ noway_assert(!varDsc->lvTracked || compiler->opts.MinOpts() || !(op1->gtFlags & GTF_VAR_DEATH));
+
+ // Does this variable live in a register?
+ //
+ if (genMarkLclVar(op1))
+ {
+ noway_assert(!compiler->opts.compDbgCode); // We don't enregister any floats with debug codegen
+
+ // Get hold of the target register
+ //
+ regNumber op1Reg = op1->gtRegVar.gtRegNum;
+
+ // the variable being assigned should be dead in op2
+ assert(!varDsc->lvTracked ||
+ !VarSetOps::IsMember(compiler, genUpdateLiveSetForward(op2), varDsc->lvVarIndex));
+
+ // Setup register preferencing, so that we try to target the op1 enregistered variable
+ //
+ regMaskTP bestMask = genRegMask(op1Reg);
+ if (type == TYP_DOUBLE)
+ {
+ assert((bestMask & RBM_DBL_REGS) != 0);
+ bestMask |= genRegMask(REG_NEXT(op1Reg));
+ }
+ RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestMask);
+
+ // Evaluate op2 into a floating point register
+ //
+ genCodeForTreeFloat(op2, &pref);
+
+ noway_assert(op2->gtFlags & GTF_REG_VAL);
+
+ // Make sure the value ends up in the right place ...
+ // For example if op2 is a call that returns a result
+ // in REG_F0, we will need to do a move instruction here
+ //
+ if ((op2->gtRegNum != op1Reg) || (op2->TypeGet() != type))
+ {
+ regMaskTP spillRegs = regSet.rsMaskUsed & genRegMaskFloat(op1Reg, op1->TypeGet());
+ if (spillRegs != 0)
+ regSet.rsSpillRegs(spillRegs);
+
+ assert(type == op1->TypeGet());
+
+ inst_RV_RV(ins_FloatConv(type, op2->TypeGet()), op1Reg, op2->gtRegNum, type);
+ }
+ genUpdateLife(op1);
+ goto DONE_ASG;
+ }
+ break;
+
+ case GT_CLS_VAR:
+ case GT_IND:
+ // Check for a volatile/unaligned store
+ //
+ assert((op1->OperGet() == GT_CLS_VAR) ||
+ (op1->OperGet() == GT_IND)); // Required for GTF_IND_VOLATILE flag to be valid
+ if (op1->gtFlags & GTF_IND_VOLATILE)
+ volat = true;
+ if (op1->gtFlags & GTF_IND_UNALIGNED)
+ unaligned = true;
+ break;
+
+ default:
+ break;
+ }
+
+ // Is the value being assigned an enregistered floating-point local variable?
+ //
+ switch (op2->gtOper)
+ {
+ case GT_LCL_VAR:
+
+ if (!genMarkLclVar(op2))
+ break;
+
+ __fallthrough;
+
+ case GT_REG_VAR:
+
+ // We must honor the order evalauation in case op1 reassigns our op2 register
+ //
+ if (tree->gtFlags & GTF_REVERSE_OPS)
+ break;
+
+ // Is there an implicit conversion that we have to insert?
+ // Handle this case with the normal cases below.
+ //
+ if (type != op2->TypeGet())
+ break;
+
+ // Make the target addressable
+ //
+ addrReg = genMakeAddressable(op1, needRegOp1, RegSet::KEEP_REG, true);
+
+ noway_assert(op2->gtFlags & GTF_REG_VAL);
+ noway_assert(op2->IsRegVar());
+
+ op2reg = op2->gtRegVar.gtRegNum;
+ genUpdateLife(op2);
+
+ goto CHK_VOLAT_UNALIGN;
+ default:
+ break;
+ }
+
+ // Is the op2 (RHS) more complex than op1 (LHS)?
+ //
+ if (tree->gtFlags & GTF_REVERSE_OPS)
+ {
+ regMaskTP bestRegs = regSet.rsNarrowHint(RBM_ALLFLOAT, ~op1->gtRsvdRegs);
+ RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestRegs);
+
+ // Generate op2 (RHS) into a floating point register
+ //
+ genCodeForTreeFloat(op2, &pref);
+ regSet.SetUsedRegFloat(op2, true);
+
+ // Make the target addressable
+ //
+ addrReg = genMakeAddressable(op1, needRegOp1, RegSet::KEEP_REG, true);
+
+ genRecoverReg(op2, RBM_ALLFLOAT, RegSet::KEEP_REG);
+ noway_assert(op2->gtFlags & GTF_REG_VAL);
+ regSet.SetUsedRegFloat(op2, false);
+ }
+ else
+ {
+ needRegOp1 = regSet.rsNarrowHint(needRegOp1, ~op2->gtRsvdRegs);
+
+ // Make the target addressable
+ //
+ addrReg = genMakeAddressable(op1, needRegOp1, RegSet::KEEP_REG, true);
+
+ // Generate the RHS into any floating point register
+ genCodeForTreeFloat(op2);
+ }
+ noway_assert(op2->gtFlags & GTF_REG_VAL);
+
+ op2reg = op2->gtRegNum;
+
+ // Is there an implicit conversion that we have to insert?
+ //
+ if (type != op2->TypeGet())
+ {
+ regMaskTP bestMask = genRegMask(op2reg);
+ if (type == TYP_DOUBLE)
+ {
+ if (bestMask & RBM_DBL_REGS)
+ {
+ bestMask |= genRegMask(REG_NEXT(op2reg));
+ }
+ else
+ {
+ bestMask |= genRegMask(REG_PREV(op2reg));
+ }
+ }
+ RegSet::RegisterPreference op2Pref(RBM_ALLFLOAT, bestMask);
+ op2reg = regSet.PickRegFloat(type, &op2Pref);
+
+ inst_RV_RV(ins_FloatConv(type, op2->TypeGet()), op2reg, op2->gtRegNum, type);
+ }
+
+ // Make sure the LHS is still addressable
+ //
+ addrReg = genKeepAddressable(op1, addrReg);
+
+CHK_VOLAT_UNALIGN:
+
+ regSet.rsLockUsedReg(addrReg); // Must prevent unaligned regSet.rsGrabReg from choosing an addrReg
+
+ if (volat)
+ {
+ // Emit a memory barrier instruction before the store
+ instGen_MemoryBarrier();
+ }
+ if (unaligned)
+ {
+ var_types storeType = op1->TypeGet();
+ assert(storeType == TYP_DOUBLE || storeType == TYP_FLOAT);
+
+ // Unaligned Floating-Point Stores must be done using the integer register(s)
+ regNumber intRegLo = regSet.rsGrabReg(RBM_ALLINT);
+ regNumber intRegHi = REG_NA;
+ regMaskTP tmpLockMask = genRegMask(intRegLo);
+
+ if (storeType == TYP_DOUBLE)
+ {
+ intRegHi = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(intRegLo));
+ tmpLockMask |= genRegMask(intRegHi);
+ }
+
+ // move the FP register over to the integer register(s)
+ //
+ if (storeType == TYP_DOUBLE)
+ {
+ getEmitter()->emitIns_R_R_R(INS_vmov_d2i, EA_8BYTE, intRegLo, intRegHi, op2reg);
+ regTracker.rsTrackRegTrash(intRegHi);
+ }
+ else
+ {
+ getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, intRegLo, op2reg);
+ }
+ regTracker.rsTrackRegTrash(intRegLo);
+
+ regSet.rsLockReg(tmpLockMask); // Temporarily lock the intRegs
+ op1->gtType = TYP_INT; // Temporarily change the type to TYP_INT
+
+ inst_TT_RV(ins_Store(TYP_INT), op1, intRegLo);
+
+ if (storeType == TYP_DOUBLE)
+ {
+ inst_TT_RV(ins_Store(TYP_INT), op1, intRegHi, 4);
+ }
+
+ op1->gtType = storeType; // Change the type back to the floating point type
+ regSet.rsUnlockReg(tmpLockMask); // Unlock the intRegs
+ }
+ else
+ {
+ // Move the value into the target
+ //
+ inst_TT_RV(ins_Store(op1->TypeGet()), op1, op2reg);
+ }
+
+ // Free up anything that was tied up by the LHS
+ //
+ regSet.rsUnlockUsedReg(addrReg);
+ genDoneAddressable(op1, addrReg, RegSet::KEEP_REG);
+
+DONE_ASG:
+
+ genUpdateLife(tree);
+
+#ifdef DEBUGGING_SUPPORT
+ /* For non-debuggable code, every definition of a lcl-var has
+ * to be checked to see if we need to open a new scope for it.
+ */
+ if (lclVarNum < compiler->lvaCount)
+ siCheckVarScope(lclVarNum, lclILoffs);
+#endif
+}
+
+void CodeGen::genCodeForTreeFloat(GenTreePtr tree, RegSet::RegisterPreference* pref)
+{
+ genTreeOps oper;
+ unsigned kind;
+
+ assert(tree);
+ assert(tree->gtOper != GT_STMT);
+
+ // What kind of node do we have?
+ oper = tree->OperGet();
+ kind = tree->OperKind();
+
+ if (kind & GTK_CONST)
+ {
+ genFloatConst(tree, pref);
+ }
+ else if (kind & GTK_LEAF)
+ {
+ genFloatLeaf(tree, pref);
+ }
+ else if (kind & GTK_SMPOP)
+ {
+ genFloatSimple(tree, pref);
+ }
+ else
+ {
+ assert(oper == GT_CALL);
+ genCodeForCall(tree, true);
+ }
+}
+
+void CodeGen::genFloatLeaf(GenTree* tree, RegSet::RegisterPreference* pref)
+{
+ regNumber reg = REG_NA;
+
+ switch (tree->OperGet())
+ {
+ case GT_LCL_VAR:
+ // Does the variable live in a register?
+ //
+ if (!genMarkLclVar(tree))
+ goto MEM_LEAF;
+ __fallthrough;
+
+ case GT_REG_VAR:
+ noway_assert(tree->gtFlags & GTF_REG_VAL);
+ reg = tree->gtRegVar.gtRegNum;
+ break;
+
+ case GT_LCL_FLD:
+ // We only use GT_LCL_FLD for lvAddrTaken vars, so we don't have
+ // to worry about it being enregistered.
+ noway_assert(compiler->lvaTable[tree->gtLclFld.gtLclNum].lvRegister == 0);
+ __fallthrough;
+
+ case GT_CLS_VAR:
+
+ MEM_LEAF:
+ reg = regSet.PickRegFloat(tree->TypeGet(), pref);
+ genLoadFloat(tree, reg);
+ break;
+
+ default:
+ DISPTREE(tree);
+ assert(!"unexpected leaf");
+ }
+
+ genCodeForTreeFloat_DONE(tree, reg);
+ return;
+}
+
+void CodeGen::genLoadFloat(GenTreePtr tree, regNumber reg)
+{
+ if (tree->IsRegVar())
+ {
+ // if it has been spilled, unspill it.%
+ LclVarDsc* varDsc = &compiler->lvaTable[tree->gtLclVarCommon.gtLclNum];
+ if (varDsc->lvSpilled)
+ {
+ UnspillFloat(varDsc);
+ }
+
+ inst_RV_RV(ins_FloatCopy(tree->TypeGet()), reg, tree->gtRegNum, tree->TypeGet());
+ }
+ else
+ {
+ bool unalignedLoad = false;
+ switch (tree->OperGet())
+ {
+ case GT_IND:
+ case GT_CLS_VAR:
+ if (tree->gtFlags & GTF_IND_UNALIGNED)
+ unalignedLoad = true;
+ break;
+ case GT_LCL_FLD:
+ // Check for a misalignment on a Floating Point field
+ //
+ if (varTypeIsFloating(tree->TypeGet()))
+ {
+ if ((tree->gtLclFld.gtLclOffs % emitTypeSize(tree->TypeGet())) != 0)
+ {
+ unalignedLoad = true;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (unalignedLoad)
+ {
+ // Make the target addressable
+ //
+ regMaskTP addrReg = genMakeAddressable(tree, 0, RegSet::KEEP_REG, true);
+ regSet.rsLockUsedReg(addrReg); // Must prevent regSet.rsGrabReg from choosing an addrReg
+
+ var_types loadType = tree->TypeGet();
+ assert(loadType == TYP_DOUBLE || loadType == TYP_FLOAT);
+
+ // Unaligned Floating-Point Loads must be loaded into integer register(s)
+ // and then moved over to the Floating-Point register
+ regNumber intRegLo = regSet.rsGrabReg(RBM_ALLINT);
+ regNumber intRegHi = REG_NA;
+ regMaskTP tmpLockMask = genRegMask(intRegLo);
+
+ if (loadType == TYP_DOUBLE)
+ {
+ intRegHi = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(intRegLo));
+ tmpLockMask |= genRegMask(intRegHi);
+ }
+
+ regSet.rsLockReg(tmpLockMask); // Temporarily lock the intRegs
+ tree->gtType = TYP_INT; // Temporarily change the type to TYP_INT
+
+ inst_RV_TT(ins_Load(TYP_INT), intRegLo, tree);
+ regTracker.rsTrackRegTrash(intRegLo);
+
+ if (loadType == TYP_DOUBLE)
+ {
+ inst_RV_TT(ins_Load(TYP_INT), intRegHi, tree, 4);
+ regTracker.rsTrackRegTrash(intRegHi);
+ }
+
+ tree->gtType = loadType; // Change the type back to the floating point type
+ regSet.rsUnlockReg(tmpLockMask); // Unlock the intRegs
+
+ // move the integer register(s) over to the FP register
+ //
+ if (loadType == TYP_DOUBLE)
+ getEmitter()->emitIns_R_R_R(INS_vmov_i2d, EA_8BYTE, reg, intRegLo, intRegHi);
+ else
+ getEmitter()->emitIns_R_R(INS_vmov_i2f, EA_4BYTE, reg, intRegLo);
+
+ // Free up anything that was tied up by genMakeAddressable
+ //
+ regSet.rsUnlockUsedReg(addrReg);
+ genDoneAddressable(tree, addrReg, RegSet::KEEP_REG);
+ }
+ else
+ {
+ inst_RV_TT(ins_FloatLoad(tree->TypeGet()), reg, tree);
+ }
+ if (((tree->OperGet() == GT_CLS_VAR) || (tree->OperGet() == GT_IND)) && (tree->gtFlags & GTF_IND_VOLATILE))
+ {
+ // Emit a memory barrier instruction after the load
+ instGen_MemoryBarrier();
+ }
+ }
+}
+
+void CodeGen::genCodeForTreeFloat_DONE(GenTreePtr tree, regNumber reg)
+{
+ return genCodeForTree_DONE(tree, reg);
+}
+
+void CodeGen::genFloatAsgArith(GenTreePtr tree)
+{
+ // Set Flowgraph.cpp, line 13750
+ // arm VFP has tons of regs, 3-op instructions, and no addressing modes
+ // so asg ops are kind of pointless
+ noway_assert(!"Not Reachable for _TARGET_ARM_");
+}
+
+regNumber CodeGen::genAssignArithFloat(
+ genTreeOps oper, GenTreePtr dst, regNumber dstreg, GenTreePtr src, regNumber srcreg)
+{
+ regNumber result;
+
+ // dst should be a regvar or memory
+
+ if (dst->IsRegVar())
+ {
+ regNumber reg = dst->gtRegNum;
+
+ if (src->IsRegVar())
+ {
+ inst_RV_RV(ins_MathOp(oper, dst->gtType), reg, src->gtRegNum, dst->gtType);
+ }
+ else
+ {
+ inst_RV_TT(ins_MathOp(oper, dst->gtType), reg, src, 0, EmitSize(dst));
+ }
+ result = reg;
+ }
+ else // dst in memory
+ {
+ // since this is an asgop the ACTUAL destination is memory
+ // but it is also one of the sources and SSE ops do not allow mem dests
+ // so we have loaded it into a reg, and that is what dstreg represents
+ assert(dstreg != REG_NA);
+
+ if ((src->InReg()))
+ {
+ inst_RV_RV(ins_MathOp(oper, dst->gtType), dstreg, src->gtRegNum, dst->gtType);
+ }
+ else
+ {
+ // mem mem operation
+ inst_RV_TT(ins_MathOp(oper, dst->gtType), dstreg, src, 0, EmitSize(dst));
+ }
+
+ dst->gtFlags &= ~GTF_REG_VAL; // ???
+
+ inst_TT_RV(ins_FloatStore(dst->gtType), dst, dstreg, 0, EmitSize(dst));
+
+ result = REG_NA;
+ }
+
+ return result;
+}
+
+void CodeGen::genFloatArith(GenTreePtr tree, RegSet::RegisterPreference* tgtPref)
+{
+ var_types type = tree->TypeGet();
+ genTreeOps oper = tree->OperGet();
+ GenTreePtr op1 = tree->gtGetOp1();
+ GenTreePtr op2 = tree->gtGetOp2();
+
+ regNumber tgtReg;
+ unsigned varNum;
+ LclVarDsc* varDsc;
+ VARSET_TP varBit;
+
+ assert(oper == GT_ADD || oper == GT_SUB || oper == GT_MUL || oper == GT_DIV);
+
+ RegSet::RegisterPreference defaultPref(RBM_ALLFLOAT, RBM_NONE);
+ if (tgtPref == NULL)
+ {
+ tgtPref = &defaultPref;
+ }
+
+ // Is the op2 (RHS)more complex than op1 (LHS)?
+ //
+ if (tree->gtFlags & GTF_REVERSE_OPS)
+ {
+ regMaskTP bestRegs = regSet.rsNarrowHint(RBM_ALLFLOAT, ~op1->gtRsvdRegs);
+ RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestRegs);
+
+ // Evaluate op2 into a floating point register
+ //
+ genCodeForTreeFloat(op2, &pref);
+ regSet.SetUsedRegFloat(op2, true);
+
+ // Evaluate op1 into any floating point register
+ //
+ genCodeForTreeFloat(op1);
+ regSet.SetUsedRegFloat(op1, true);
+
+ regNumber op1Reg = op1->gtRegNum;
+ regMaskTP op1Mask = genRegMaskFloat(op1Reg, type);
+
+ // Fix 388445 ARM JitStress WP7
+ regSet.rsLockUsedReg(op1Mask);
+ genRecoverReg(op2, RBM_ALLFLOAT, RegSet::KEEP_REG);
+ noway_assert(op2->gtFlags & GTF_REG_VAL);
+ regSet.rsUnlockUsedReg(op1Mask);
+
+ regSet.SetUsedRegFloat(op1, false);
+ regSet.SetUsedRegFloat(op2, false);
+ }
+ else
+ {
+ regMaskTP bestRegs = regSet.rsNarrowHint(RBM_ALLFLOAT, ~op2->gtRsvdRegs);
+ RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestRegs);
+
+ // Evaluate op1 into a floating point register
+ //
+ genCodeForTreeFloat(op1, &pref);
+ regSet.SetUsedRegFloat(op1, true);
+
+ // Evaluate op2 into any floating point register
+ //
+ genCodeForTreeFloat(op2);
+ regSet.SetUsedRegFloat(op2, true);
+
+ regNumber op2Reg = op2->gtRegNum;
+ regMaskTP op2Mask = genRegMaskFloat(op2Reg, type);
+
+ // Fix 388445 ARM JitStress WP7
+ regSet.rsLockUsedReg(op2Mask);
+ genRecoverReg(op1, RBM_ALLFLOAT, RegSet::KEEP_REG);
+ noway_assert(op1->gtFlags & GTF_REG_VAL);
+ regSet.rsUnlockUsedReg(op2Mask);
+
+ regSet.SetUsedRegFloat(op2, false);
+ regSet.SetUsedRegFloat(op1, false);
+ }
+
+ tgtReg = regSet.PickRegFloat(type, tgtPref, true);
+
+ noway_assert(op1->gtFlags & GTF_REG_VAL);
+ noway_assert(op2->gtFlags & GTF_REG_VAL);
+
+ inst_RV_RV_RV(ins_MathOp(oper, type), tgtReg, op1->gtRegNum, op2->gtRegNum, emitActualTypeSize(type));
+
+ genCodeForTreeFloat_DONE(tree, tgtReg);
+}
+
+regNumber CodeGen::genArithmFloat(
+ genTreeOps oper, GenTreePtr dst, regNumber dstreg, GenTreePtr src, regNumber srcreg, bool bReverse)
+{
+ regNumber result = REG_NA;
+
+ assert(dstreg != REG_NA);
+
+ if (bReverse)
+ {
+ GenTree* temp = src;
+ regNumber tempreg = srcreg;
+ src = dst;
+ srcreg = dstreg;
+ dst = temp;
+ dstreg = tempreg;
+ }
+
+ if (srcreg == REG_NA)
+ {
+ if (src->IsRegVar())
+ {
+ inst_RV_RV(ins_MathOp(oper, dst->gtType), dst->gtRegNum, src->gtRegNum, dst->gtType);
+ }
+ else
+ {
+ inst_RV_TT(ins_MathOp(oper, dst->gtType), dst->gtRegNum, src);
+ }
+ }
+ else
+ {
+ inst_RV_RV(ins_MathOp(oper, dst->gtType), dstreg, srcreg, dst->gtType);
+ }
+
+ result = dstreg;
+
+ assert(result != REG_NA);
+ return result;
+}
+
+void CodeGen::genKeepAddressableFloat(GenTreePtr tree, regMaskTP* regMaskIntPtr, regMaskTP* regMaskFltPtr)
+{
+ regMaskTP regMaskInt, regMaskFlt;
+
+ regMaskInt = *regMaskIntPtr;
+ regMaskFlt = *regMaskFltPtr;
+
+ *regMaskIntPtr = *regMaskFltPtr = 0;
+
+ switch (tree->OperGet())
+ {
+ case GT_REG_VAR:
+ // If register has been spilled, unspill it
+ if (tree->gtFlags & GTF_SPILLED)
+ {
+ UnspillFloat(&compiler->lvaTable[tree->gtLclVarCommon.gtLclNum]);
+ }
+ break;
+
+ case GT_CNS_DBL:
+ if (tree->gtFlags & GTF_SPILLED)
+ {
+ UnspillFloat(tree);
+ }
+ *regMaskFltPtr = genRegMaskFloat(tree->gtRegNum, tree->TypeGet());
+ break;
+
+ case GT_LCL_FLD:
+ case GT_LCL_VAR:
+ case GT_CLS_VAR:
+ break;
+
+ case GT_IND:
+ if (regMaskFlt == RBM_NONE)
+ {
+ *regMaskIntPtr = genKeepAddressable(tree, regMaskInt, 0);
+ *regMaskFltPtr = 0;
+ return;
+ }
+ __fallthrough;
+
+ default:
+ *regMaskIntPtr = 0;
+ if (tree->gtFlags & GTF_SPILLED)
+ {
+ UnspillFloat(tree);
+ }
+ *regMaskFltPtr = genRegMaskFloat(tree->gtRegNum, tree->TypeGet());
+ break;
+ }
+}
+
+void CodeGen::genComputeAddressableFloat(GenTreePtr tree,
+ regMaskTP addrRegInt,
+ regMaskTP addrRegFlt,
+ RegSet::KeepReg keptReg,
+ regMaskTP needReg,
+ RegSet::KeepReg keepReg,
+ bool freeOnly /* = false */)
+{
+ noway_assert(genStillAddressable(tree));
+ noway_assert(varTypeIsFloating(tree->TypeGet()));
+
+ genDoneAddressableFloat(tree, addrRegInt, addrRegFlt, keptReg);
+
+ regNumber reg;
+ if (tree->gtFlags & GTF_REG_VAL)
+ {
+ reg = tree->gtRegNum;
+ if (freeOnly && !(genRegMaskFloat(reg, tree->TypeGet()) & regSet.RegFreeFloat()))
+ {
+ goto LOAD_REG;
+ }
+ }
+ else
+ {
+ LOAD_REG:
+ RegSet::RegisterPreference pref(needReg, RBM_NONE);
+ reg = regSet.PickRegFloat(tree->TypeGet(), &pref);
+ genLoadFloat(tree, reg);
+ }
+
+ genMarkTreeInReg(tree, reg);
+
+ if (keepReg == RegSet::KEEP_REG)
+ {
+ regSet.SetUsedRegFloat(tree, true);
+ }
+}
+
+void CodeGen::genDoneAddressableFloat(GenTreePtr tree,
+ regMaskTP addrRegInt,
+ regMaskTP addrRegFlt,
+ RegSet::KeepReg keptReg)
+{
+ assert(!(addrRegInt && addrRegFlt));
+
+ if (addrRegInt)
+ {
+ return genDoneAddressable(tree, addrRegInt, keptReg);
+ }
+ else if (addrRegFlt)
+ {
+ if (keptReg == RegSet::KEEP_REG)
+ {
+ for (regNumber r = REG_FP_FIRST; r != REG_NA; r = regNextOfType(r, tree->TypeGet()))
+ {
+ regMaskTP mask = genRegMaskFloat(r, tree->TypeGet());
+ // some masks take up more than one bit
+ if ((mask & addrRegFlt) == mask)
+ {
+ regSet.SetUsedRegFloat(tree, false);
+ }
+ }
+ }
+ }
+}
+
+GenTreePtr CodeGen::genMakeAddressableFloat(GenTreePtr tree,
+ regMaskTP* regMaskIntPtr,
+ regMaskTP* regMaskFltPtr,
+ bool bCollapseConstantDoubles)
+{
+ *regMaskIntPtr = *regMaskFltPtr = 0;
+
+ switch (tree->OperGet())
+ {
+
+ case GT_LCL_VAR:
+ genMarkLclVar(tree);
+ __fallthrough;
+
+ case GT_REG_VAR:
+ case GT_LCL_FLD:
+ case GT_CLS_VAR:
+ return tree;
+
+ case GT_IND:
+ // Try to make the address directly addressable
+
+ if (genMakeIndAddrMode(tree->gtOp.gtOp1, tree, false, RBM_ALLFLOAT, RegSet::KEEP_REG, regMaskIntPtr, false))
+ {
+ genUpdateLife(tree);
+ return tree;
+ }
+ else
+ {
+ GenTreePtr addr = tree;
+ tree = tree->gtOp.gtOp1;
+ genCodeForTree(tree, 0);
+ regSet.rsMarkRegUsed(tree, addr);
+
+ *regMaskIntPtr = genRegMask(tree->gtRegNum);
+ return addr;
+ }
+
+ // fall through
+
+ default:
+ genCodeForTreeFloat(tree);
+ regSet.SetUsedRegFloat(tree, true);
+
+ // update mask
+ *regMaskFltPtr = genRegMaskFloat(tree->gtRegNum, tree->TypeGet());
+
+ return tree;
+ break;
+ }
+}
+
+void CodeGen::genCodeForTreeCastFloat(GenTree* tree, RegSet::RegisterPreference* pref)
+{
+ GenTreePtr op1 = tree->gtOp.gtOp1;
+ var_types from = op1->gtType;
+ var_types to = tree->gtType;
+
+ if (varTypeIsFloating(from))
+ genCodeForTreeCastFromFloat(tree, pref);
+ else
+ genCodeForTreeCastToFloat(tree, pref);
+}
+
+void CodeGen::genCodeForTreeCastFromFloat(GenTree* tree, RegSet::RegisterPreference* pref)
+{
+ GenTreePtr op1 = tree->gtOp.gtOp1;
+ var_types from = op1->gtType;
+ var_types final = tree->gtType;
+ var_types intermediate = tree->CastToType();
+
+ regNumber srcReg;
+ regNumber dstReg;
+
+ assert(varTypeIsFloating(from));
+
+ // Evaluate op1 into a floating point register
+ //
+ if (varTypeIsFloating(final))
+ {
+ genCodeForTreeFloat(op1, pref);
+ }
+ else
+ {
+ RegSet::RegisterPreference defaultPref(RBM_ALLFLOAT, RBM_NONE);
+ genCodeForTreeFloat(op1, &defaultPref);
+ }
+
+ srcReg = op1->gtRegNum;
+
+ if (varTypeIsFloating(final))
+ {
+ // float => double or
+ // double => float
+
+ dstReg = regSet.PickRegFloat(final, pref);
+
+ instruction ins = ins_FloatConv(final, from);
+ if (!isMoveIns(ins) || (srcReg != dstReg))
+ {
+ inst_RV_RV(ins, dstReg, srcReg, from);
+ }
+ }
+ else
+ {
+ // float => int or
+ // double => int
+
+ dstReg = regSet.rsPickReg(pref->ok, pref->best);
+
+ RegSet::RegisterPreference defaultPref(RBM_ALLFLOAT, genRegMask(srcReg));
+ regNumber intermediateReg = regSet.PickRegFloat(TYP_FLOAT, &defaultPref);
+
+ if ((intermediate == TYP_UINT) && (final == TYP_INT))
+ {
+ // Perform the conversion using the FP unit
+ inst_RV_RV(ins_FloatConv(TYP_UINT, from), intermediateReg, srcReg, from);
+
+ // Prevent the call to genIntegerCast
+ final = TYP_UINT;
+ }
+ else
+ {
+ // Perform the conversion using the FP unit
+ inst_RV_RV(ins_FloatConv(TYP_INT, from), intermediateReg, srcReg, from);
+ }
+
+ // the integer result is now in the FP register, move it to the integer ones
+ getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, dstReg, intermediateReg);
+
+ regTracker.rsTrackRegTrash(dstReg);
+
+ // handle things like int <- short <- double
+ if (final != intermediate)
+ {
+ // lie about the register so integer cast logic will finish the job
+ op1->gtRegNum = dstReg;
+ genIntegerCast(tree, pref->ok, pref->best);
+ }
+ }
+
+ genUpdateLife(op1);
+ genCodeForTree_DONE(tree, dstReg);
+}
+
+void CodeGen::genCodeForTreeCastToFloat(GenTreePtr tree, RegSet::RegisterPreference* pref)
+{
+ regNumber srcReg;
+ regNumber dstReg;
+ regNumber vmovReg;
+
+ regMaskTP addrReg;
+
+ GenTreePtr op1 = tree->gtOp.gtOp1;
+ op1 = genCodeForCommaTree(op1); // Trim off any comma expressions.
+ var_types from = op1->gtType;
+ var_types to = tree->gtType;
+
+ switch (from)
+ {
+ case TYP_BOOL:
+ case TYP_BYTE:
+ case TYP_UBYTE:
+ case TYP_CHAR:
+ case TYP_SHORT:
+ // load it into a register
+ genCodeForTree(op1, 0);
+
+ __fallthrough;
+
+ case TYP_BYREF:
+ from = TYP_INT;
+
+ __fallthrough;
+
+ case TYP_INT:
+ {
+ if (op1->gtOper == GT_LCL_FLD)
+ {
+ genComputeReg(op1, 0, RegSet::ANY_REG, RegSet::FREE_REG);
+ addrReg = 0;
+ }
+ else
+ {
+ addrReg = genMakeAddressable(op1, 0, RegSet::FREE_REG);
+ }
+
+ // Grab register for the cast
+ dstReg = regSet.PickRegFloat(to, pref);
+
+ // float type that is same size as the int we are coming from
+ var_types vmovType = TYP_FLOAT;
+ regNumber vmovReg = regSet.PickRegFloat(vmovType);
+
+ if (tree->gtFlags & GTF_UNSIGNED)
+ from = TYP_UINT;
+
+ // Is the value a constant, or now sitting in a register?
+ if (op1->InReg() || op1->IsCnsIntOrI())
+ {
+ if (op1->IsCnsIntOrI())
+ {
+ srcReg = genGetRegSetToIcon(op1->AsIntConCommon()->IconValue(), RBM_NONE, op1->TypeGet());
+ }
+ else
+ {
+ srcReg = op1->gtRegNum;
+ }
+
+ // move the integer register value over to the FP register
+ getEmitter()->emitIns_R_R(INS_vmov_i2f, EA_4BYTE, vmovReg, srcReg);
+ // now perform the conversion to the proper floating point representation
+ inst_RV_RV(ins_FloatConv(to, from), dstReg, vmovReg, to);
+ }
+ else
+ {
+ // Load the value from its address
+ inst_RV_TT(ins_FloatLoad(vmovType), vmovReg, op1);
+ inst_RV_RV(ins_FloatConv(to, from), dstReg, vmovReg, to);
+ }
+
+ if (addrReg)
+ {
+ genDoneAddressable(op1, addrReg, RegSet::FREE_REG);
+ }
+ genMarkTreeInReg(tree, dstReg);
+
+ break;
+ }
+ case TYP_FLOAT:
+ case TYP_DOUBLE:
+ {
+ // This is a cast from float to double or double to float
+
+ genCodeForTreeFloat(op1, pref);
+
+ // Grab register for the cast
+ dstReg = regSet.PickRegFloat(to, pref);
+
+ if ((from != to) || (dstReg != op1->gtRegNum))
+ {
+ inst_RV_RV(ins_FloatConv(to, from), dstReg, op1->gtRegNum, to);
+ }
+
+ // Assign reg to tree
+ genMarkTreeInReg(tree, dstReg);
+
+ break;
+ }
+ default:
+ {
+ assert(!"unsupported cast");
+ break;
+ }
+ }
+}
+
+void CodeGen::genRoundFloatExpression(GenTreePtr op, var_types type)
+{
+ // Do nothing with memory resident opcodes - these are the right precision
+ if (type == TYP_UNDEF)
+ type = op->TypeGet();
+
+ switch (op->gtOper)
+ {
+ case GT_LCL_VAR:
+ genMarkLclVar(op);
+ __fallthrough;
+
+ case GT_LCL_FLD:
+ case GT_CLS_VAR:
+ case GT_CNS_DBL:
+ case GT_IND:
+ if (type == op->TypeGet())
+ return;
+
+ default:
+ break;
+ }
+}
+
+#ifdef DEBUG
+
+regMaskTP CodeGenInterface::genStressLockedMaskFloat()
+{
+ return 0;
+}
+
+#endif // DEBUG
+
+/*********************************************************************
+ * Preserve used callee trashed registers across calls.
+ *
+ */
+void CodeGen::SpillForCallRegisterFP(regMaskTP noSpillMask)
+{
+ regMaskTP regBit = 1;
+ for (regNumber regNum = REG_FIRST; regNum < REG_COUNT; regNum = REG_NEXT(regNum), regBit <<= 1)
+ {
+ if (!(regBit & noSpillMask) && (regBit & RBM_FLT_CALLEE_TRASH) && regSet.rsUsedTree[regNum])
+ {
+ SpillFloat(regNum, true);
+ }
+ }
+}
+
+/*********************************************************************
+ *
+ * Spill the used floating point register or the enregistered var.
+ * If spilling for a call, then record so, so we can unspill the
+ * ones that were spilled for the call.
+ *
+ */
+void CodeGenInterface::SpillFloat(regNumber reg, bool bIsCall /* = false */)
+{
+ regSet.rsSpillReg(reg);
+}
+
+void CodeGen::UnspillFloatMachineDep(RegSet::SpillDsc* spillDsc)
+{
+ // Do actual unspill
+ regNumber reg;
+ if (spillDsc->bEnregisteredVariable)
+ {
+ NYI("unspill enreg var");
+ reg = regSet.PickRegFloat();
+ }
+ else
+ {
+ UnspillFloatMachineDep(spillDsc, false);
+ }
+}
+
+void CodeGen::UnspillFloatMachineDep(RegSet::SpillDsc* spillDsc, bool useSameReg)
+{
+ assert(!spillDsc->bEnregisteredVariable);
+
+ assert(spillDsc->spillTree->gtFlags & GTF_SPILLED);
+
+ spillDsc->spillTree->gtFlags &= ~GTF_SPILLED;
+
+ var_types type = spillDsc->spillTree->TypeGet();
+ regNumber reg;
+ if (useSameReg)
+ {
+ // Give register preference as the same register that the tree was originally using.
+ reg = spillDsc->spillTree->gtRegNum;
+
+ regMaskTP maskPref = genRegMask(reg);
+ if (type == TYP_DOUBLE)
+ {
+ assert((maskPref & RBM_DBL_REGS) != 0);
+ maskPref |= genRegMask(REG_NEXT(reg));
+ }
+
+ RegSet::RegisterPreference pref(RBM_ALLFLOAT, maskPref);
+ reg = regSet.PickRegFloat(type, &pref);
+ }
+ else
+ {
+ reg = regSet.PickRegFloat();
+ }
+
+ // load from spilled spot
+ compiler->codeGen->reloadFloatReg(type, spillDsc->spillTemp, reg);
+
+ compiler->codeGen->genMarkTreeInReg(spillDsc->spillTree, reg);
+ regSet.SetUsedRegFloat(spillDsc->spillTree, true);
+}
+
+//
+instruction genFloatJumpInstr(genTreeOps cmp, bool isUnordered)
+{
+ switch (cmp)
+ {
+ case GT_EQ:
+ return INS_beq;
+ case GT_NE:
+ return INS_bne;
+ case GT_LT:
+ return isUnordered ? INS_blt : INS_blo;
+ case GT_LE:
+ return isUnordered ? INS_ble : INS_bls;
+ case GT_GE:
+ return isUnordered ? INS_bpl : INS_bge;
+ case GT_GT:
+ return isUnordered ? INS_bhi : INS_bgt;
+ default:
+ unreached();
+ }
+}
+
+void CodeGen::genCondJumpFloat(GenTreePtr cond, BasicBlock* jumpTrue, BasicBlock* jumpFalse)
+{
+ assert(jumpTrue && jumpFalse);
+ assert(!(cond->gtFlags & GTF_REVERSE_OPS)); // Done in genCondJump()
+ assert(varTypeIsFloating(cond->gtOp.gtOp1->gtType));
+
+ GenTreePtr op1 = cond->gtOp.gtOp1;
+ GenTreePtr op2 = cond->gtOp.gtOp2;
+ genTreeOps cmp = cond->OperGet();
+ bool isUnordered = cond->gtFlags & GTF_RELOP_NAN_UN ? true : false;
+
+ regMaskTP bestRegs = regSet.rsNarrowHint(RBM_ALLFLOAT, ~op2->gtRsvdRegs);
+ RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestRegs);
+
+ // Prepare operands.
+ genCodeForTreeFloat(op1, &pref);
+ regSet.SetUsedRegFloat(op1, true);
+
+ genCodeForTreeFloat(op2);
+ regSet.SetUsedRegFloat(op2, true);
+
+ genRecoverReg(op1, RBM_ALLFLOAT, RegSet::KEEP_REG);
+ noway_assert(op1->gtFlags & GTF_REG_VAL);
+
+ // cmp here
+ getEmitter()->emitIns_R_R(INS_vcmp, EmitSize(op1), op1->gtRegNum, op2->gtRegNum);
+
+ // vmrs with register 0xf has special meaning of transferring flags
+ getEmitter()->emitIns_R(INS_vmrs, EA_4BYTE, REG_R15);
+
+ regSet.SetUsedRegFloat(op2, false);
+ regSet.SetUsedRegFloat(op1, false);
+
+ getEmitter()->emitIns_J(genFloatJumpInstr(cmp, isUnordered), jumpTrue);
+}
+
+#endif // LEGACY_BACKEND