1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
10 #ifdef LEGACY_BACKEND // This file is NOT used for the RyuJIT backend that uses the linear scan register allocator.
17 #error "Non-ARM target for registerfp.cpp"
18 #endif // !_TARGET_ARM_
20 // get the next argument register which is aligned to 'alignment' # of bytes
21 regNumber alignFloatArgReg(regNumber argReg, int alignment)
23 assert(isValidFloatArgReg(argReg));
25 int regsize_alignment = alignment /= REGSIZE_BYTES;
26 if (genMapFloatRegNumToRegArgNum(argReg) % regsize_alignment)
27 argReg = genRegArgNext(argReg);
29 // technically the above should be a 'while' so make sure
30 // we never should have incremented more than once
31 assert(!(genMapFloatRegNumToRegArgNum(argReg) % regsize_alignment));
37 // N=normal, R=reverse, P=pop
39 void CodeGen::genFloatConst(GenTree* tree, RegSet::RegisterPreference* pref)
41 assert(tree->gtOper == GT_CNS_DBL);
42 var_types type = tree->gtType;
43 double constValue = tree->gtDblCon.gtDconVal;
44 size_t* cv = (size_t*)&constValue;
46 regNumber dst = regSet.PickRegFloat(type, pref);
48 if (type == TYP_FLOAT)
50 regNumber reg = regSet.rsPickReg();
52 float f = forceCastToFloat(constValue);
53 genSetRegToIcon(reg, *((int*)(&f)));
54 getEmitter()->emitIns_R_R(INS_vmov_i2f, EA_4BYTE, dst, reg);
58 assert(type == TYP_DOUBLE);
59 regNumber reg1 = regSet.rsPickReg();
60 regNumber reg2 = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(reg1));
62 genSetRegToIcon(reg1, cv[0]);
63 regSet.rsLockReg(genRegMask(reg1));
64 genSetRegToIcon(reg2, cv[1]);
65 regSet.rsUnlockReg(genRegMask(reg1));
67 getEmitter()->emitIns_R_R_R(INS_vmov_i2d, EA_8BYTE, dst, reg1, reg2);
69 genMarkTreeInReg(tree, dst);
74 void CodeGen::genFloatMath(GenTree* tree, RegSet::RegisterPreference* pref)
76 assert(tree->OperGet() == GT_INTRINSIC);
78 GenTree* op1 = tree->gtOp.gtOp1;
80 // get tree into a register
81 genCodeForTreeFloat(op1, pref);
85 switch (tree->gtIntrinsic.gtIntrinsicId)
87 case CORINFO_INTRINSIC_Sin:
90 case CORINFO_INTRINSIC_Cos:
93 case CORINFO_INTRINSIC_Sqrt:
96 case CORINFO_INTRINSIC_Abs:
99 case CORINFO_INTRINSIC_Round:
101 regNumber reg = regSet.PickRegFloat(tree->TypeGet(), pref);
102 genMarkTreeInReg(tree, reg);
103 // convert it to a long and back
104 inst_RV_RV(ins_FloatConv(TYP_LONG, tree->TypeGet()), reg, op1->gtRegNum, tree->TypeGet());
105 inst_RV_RV(ins_FloatConv(tree->TypeGet(), TYP_LONG), reg, reg);
106 genCodeForTreeFloat_DONE(tree, op1->gtRegNum);
114 if (ins != INS_invalid)
116 regNumber reg = regSet.PickRegFloat(tree->TypeGet(), pref);
117 genMarkTreeInReg(tree, reg);
118 inst_RV_RV(ins, reg, op1->gtRegNum, tree->TypeGet());
119 // mark register that holds tree
120 genCodeForTreeFloat_DONE(tree, reg);
125 // If unreached is removed, mark register that holds tree
126 // genCodeForTreeFloat_DONE(tree, op1->gtRegNum);
132 void CodeGen::genFloatSimple(GenTree* tree, RegSet::RegisterPreference* pref)
134 assert(tree->OperKind() & GTK_SMPOP);
135 var_types type = tree->TypeGet();
137 RegSet::RegisterPreference defaultPref(RBM_ALLFLOAT, RBM_NONE);
143 switch (tree->OperGet())
148 genFloatAssign(tree);
158 genFloatArith(tree, pref);
164 GenTree* op1 = tree->gtOp.gtOp1;
166 // get the tree into a register
167 genCodeForTreeFloat(op1, pref);
170 regNumber reg = regSet.PickRegFloat(type, pref);
171 genMarkTreeInReg(tree, reg);
172 inst_RV_RV(ins_MathOp(tree->OperGet(), type), reg, op1->gtRegNum, type);
174 // mark register that holds tree
175 genCodeForTreeFloat_DONE(tree, reg);
183 // Make sure the address value is 'addressable' */
184 addrReg = genMakeAddressable(tree, 0, RegSet::FREE_REG);
186 // Load the value onto the FP stack
187 regNumber reg = regSet.PickRegFloat(type, pref);
188 genLoadFloat(tree, reg);
190 genDoneAddressable(tree, addrReg, RegSet::FREE_REG);
192 genCodeForTreeFloat_DONE(tree, reg);
198 genCodeForTreeCastFloat(tree, pref);
202 // Asg-Arithmetic ops
208 genFloatAsgArith(tree);
212 genFloatMath(tree, pref);
217 GenTree* op1 = tree->gtOp.gtOp1;
220 pref->best = (type == TYP_DOUBLE) ? RBM_DOUBLERET : RBM_FLOATRET;
222 // Compute the result
223 genCodeForTreeFloat(op1, pref);
225 inst_RV_TT(ins_FloatConv(tree->TypeGet(), op1->TypeGet()), REG_FLOATRET, op1);
226 if (compiler->info.compIsVarArgs || compiler->opts.compUseSoftFP)
228 if (tree->TypeGet() == TYP_FLOAT)
230 inst_RV_RV(INS_vmov_f2i, REG_INTRET, REG_FLOATRET, TYP_FLOAT, EA_4BYTE);
234 assert(tree->TypeGet() == TYP_DOUBLE);
235 inst_RV_RV_RV(INS_vmov_d2i, REG_INTRET, REG_NEXT(REG_INTRET), REG_FLOATRET, EA_8BYTE);
245 GenTree* op1 = tree->gtOp.gtOp1;
246 GenTree* op2 = tree->gtGetOp2IfPresent();
248 if (tree->gtFlags & GTF_REVERSE_OPS)
250 genCodeForTreeFloat(op2, pref);
252 regSet.SetUsedRegFloat(op2, true);
253 genEvalSideEffects(op1);
254 regSet.SetUsedRegFloat(op2, false);
258 genEvalSideEffects(op1);
259 genCodeForTreeFloat(op2, pref);
262 genCodeForTreeFloat_DONE(tree, op2->gtRegNum);
267 genFloatCheckFinite(tree, pref);
271 NYI("Unhandled register FP codegen");
275 // generate code for ckfinite tree/instruction
276 void CodeGen::genFloatCheckFinite(GenTree* tree, RegSet::RegisterPreference* pref)
281 GenTree* op1 = tree->gtOp.gtOp1;
283 // Offset of the DWord containing the exponent
284 offs = (op1->gtType == TYP_FLOAT) ? 0 : sizeof(int);
286 // get tree into a register
287 genCodeForTreeFloat(op1, pref);
289 regNumber reg = regSet.rsPickReg();
292 if (op1->gtType == TYP_FLOAT)
294 getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, reg, op1->gtRegNum);
295 expMask = 0x7F800000;
299 assert(op1->gtType == TYP_DOUBLE);
300 getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, reg,
301 REG_NEXT(op1->gtRegNum)); // the high 32 bits of the double register
302 expMask = 0x7FF00000;
304 regTracker.rsTrackRegTrash(reg);
306 // Check if the exponent is all ones
307 inst_RV_IV(INS_and, reg, expMask, EA_4BYTE);
308 inst_RV_IV(INS_cmp, reg, expMask, EA_4BYTE);
310 // If exponent was all 1's, we need to throw ArithExcep
311 emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED);
312 genJumpToThrowHlpBlk(jmpEqual, SCK_ARITH_EXCPN);
314 genCodeForTreeFloat_DONE(tree, op1->gtRegNum);
317 void CodeGen::genFloatAssign(GenTree* tree)
319 var_types type = tree->TypeGet();
320 GenTree* op1 = tree->gtGetOp1();
321 GenTree* op2 = tree->gtGetOp2IfPresent();
323 regMaskTP needRegOp1 = RBM_ALLINT;
324 regMaskTP addrReg = RBM_NONE;
325 bool volat = false; // Is this a volatile store
326 bool unaligned = false; // Is this an unaligned store
327 regNumber op2reg = REG_NA;
329 unsigned lclVarNum = compiler->lvaCount;
330 unsigned lclILoffs = DUMMY_INIT(0);
332 noway_assert(tree->OperGet() == GT_ASG);
334 // Is the target a floating-point local variable?
335 // possibly even an enregistered floating-point local variable?
343 // Check for a misalignment on a Floating Point field
345 if (varTypeIsFloating(op1->TypeGet()))
347 if ((op1->gtLclFld.gtLclOffs % emitTypeSize(op1->TypeGet())) != 0)
355 varNum = op1->gtLclVarCommon.gtLclNum;
356 noway_assert(varNum < compiler->lvaCount);
357 varDsc = compiler->lvaTable + varNum;
359 // For non-debuggable code, every definition of a lcl-var has
360 // to be checked to see if we need to open a new scope for it.
361 // Remember the local var info to call siCheckVarScope
362 // AFTER code generation of the assignment.
364 if (compiler->opts.compScopeInfo && !compiler->opts.compDbgCode && (compiler->info.compVarScopesCount > 0))
367 lclILoffs = op1->gtLclVar.gtLclILoffs;
370 // Dead Store assert (with min opts we may have dead stores)
372 noway_assert(!varDsc->lvTracked || compiler->opts.MinOpts() || !(op1->gtFlags & GTF_VAR_DEATH));
374 // Does this variable live in a register?
376 if (genMarkLclVar(op1))
378 noway_assert(!compiler->opts.compDbgCode); // We don't enregister any floats with debug codegen
380 // Get hold of the target register
382 regNumber op1Reg = op1->gtRegVar.gtRegNum;
384 // the variable being assigned should be dead in op2
385 assert(!varDsc->lvTracked ||
386 !VarSetOps::IsMember(compiler, genUpdateLiveSetForward(op2), varDsc->lvVarIndex));
388 // Setup register preferencing, so that we try to target the op1 enregistered variable
390 regMaskTP bestMask = genRegMask(op1Reg);
391 if (type == TYP_DOUBLE)
393 assert((bestMask & RBM_DBL_REGS) != 0);
394 bestMask |= genRegMask(REG_NEXT(op1Reg));
396 RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestMask);
398 // Evaluate op2 into a floating point register
400 genCodeForTreeFloat(op2, &pref);
402 noway_assert(op2->InReg());
404 // Make sure the value ends up in the right place ...
405 // For example if op2 is a call that returns a result
406 // in REG_F0, we will need to do a move instruction here
408 if ((op2->gtRegNum != op1Reg) || (op2->TypeGet() != type))
410 regMaskTP spillRegs = regSet.rsMaskUsed & genRegMaskFloat(op1Reg, op1->TypeGet());
412 regSet.rsSpillRegs(spillRegs);
414 assert(type == op1->TypeGet());
416 inst_RV_RV(ins_FloatConv(type, op2->TypeGet()), op1Reg, op2->gtRegNum, type);
425 // Check for a volatile/unaligned store
427 assert((op1->OperGet() == GT_CLS_VAR) ||
428 (op1->OperGet() == GT_IND)); // Required for GTF_IND_VOLATILE flag to be valid
429 if (op1->gtFlags & GTF_IND_VOLATILE)
431 if (op1->gtFlags & GTF_IND_UNALIGNED)
439 // Is the value being assigned an enregistered floating-point local variable?
445 if (!genMarkLclVar(op2))
452 // We must honor the order evalauation in case op1 reassigns our op2 register
454 if (tree->gtFlags & GTF_REVERSE_OPS)
457 // Is there an implicit conversion that we have to insert?
458 // Handle this case with the normal cases below.
460 if (type != op2->TypeGet())
463 // Make the target addressable
465 addrReg = genMakeAddressable(op1, needRegOp1, RegSet::KEEP_REG, true);
467 noway_assert(op2->InReg());
468 noway_assert(op2->IsRegVar());
470 op2reg = op2->gtRegVar.gtRegNum;
473 goto CHK_VOLAT_UNALIGN;
478 // Is the op2 (RHS) more complex than op1 (LHS)?
480 if (tree->gtFlags & GTF_REVERSE_OPS)
482 regMaskTP bestRegs = regSet.rsNarrowHint(RBM_ALLFLOAT, ~op1->gtRsvdRegs);
483 RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestRegs);
485 // Generate op2 (RHS) into a floating point register
487 genCodeForTreeFloat(op2, &pref);
488 regSet.SetUsedRegFloat(op2, true);
490 // Make the target addressable
492 addrReg = genMakeAddressable(op1, needRegOp1, RegSet::KEEP_REG, true);
494 genRecoverReg(op2, RBM_ALLFLOAT, RegSet::KEEP_REG);
495 noway_assert(op2->InReg());
496 regSet.SetUsedRegFloat(op2, false);
500 needRegOp1 = regSet.rsNarrowHint(needRegOp1, ~op2->gtRsvdRegs);
502 // Make the target addressable
504 addrReg = genMakeAddressable(op1, needRegOp1, RegSet::KEEP_REG, true);
506 // Generate the RHS into any floating point register
507 genCodeForTreeFloat(op2);
509 noway_assert(op2->InReg());
511 op2reg = op2->gtRegNum;
513 // Is there an implicit conversion that we have to insert?
515 if (type != op2->TypeGet())
517 regMaskTP bestMask = genRegMask(op2reg);
518 if (type == TYP_DOUBLE)
520 if (bestMask & RBM_DBL_REGS)
522 bestMask |= genRegMask(REG_NEXT(op2reg));
526 bestMask |= genRegMask(REG_PREV(op2reg));
529 RegSet::RegisterPreference op2Pref(RBM_ALLFLOAT, bestMask);
530 op2reg = regSet.PickRegFloat(type, &op2Pref);
532 inst_RV_RV(ins_FloatConv(type, op2->TypeGet()), op2reg, op2->gtRegNum, type);
535 // Make sure the LHS is still addressable
537 addrReg = genKeepAddressable(op1, addrReg);
541 regSet.rsLockUsedReg(addrReg); // Must prevent unaligned regSet.rsGrabReg from choosing an addrReg
545 // Emit a memory barrier instruction before the store
546 instGen_MemoryBarrier();
550 var_types storeType = op1->TypeGet();
551 assert(storeType == TYP_DOUBLE || storeType == TYP_FLOAT);
553 // Unaligned Floating-Point Stores must be done using the integer register(s)
554 regNumber intRegLo = regSet.rsGrabReg(RBM_ALLINT);
555 regNumber intRegHi = REG_NA;
556 regMaskTP tmpLockMask = genRegMask(intRegLo);
558 if (storeType == TYP_DOUBLE)
560 intRegHi = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(intRegLo));
561 tmpLockMask |= genRegMask(intRegHi);
564 // move the FP register over to the integer register(s)
566 if (storeType == TYP_DOUBLE)
568 getEmitter()->emitIns_R_R_R(INS_vmov_d2i, EA_8BYTE, intRegLo, intRegHi, op2reg);
569 regTracker.rsTrackRegTrash(intRegHi);
573 getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, intRegLo, op2reg);
575 regTracker.rsTrackRegTrash(intRegLo);
577 regSet.rsLockReg(tmpLockMask); // Temporarily lock the intRegs
578 op1->gtType = TYP_INT; // Temporarily change the type to TYP_INT
580 inst_TT_RV(ins_Store(TYP_INT), op1, intRegLo);
582 if (storeType == TYP_DOUBLE)
584 inst_TT_RV(ins_Store(TYP_INT), op1, intRegHi, 4);
587 op1->gtType = storeType; // Change the type back to the floating point type
588 regSet.rsUnlockReg(tmpLockMask); // Unlock the intRegs
592 // Move the value into the target
594 inst_TT_RV(ins_Store(op1->TypeGet()), op1, op2reg);
597 // Free up anything that was tied up by the LHS
599 regSet.rsUnlockUsedReg(addrReg);
600 genDoneAddressable(op1, addrReg, RegSet::KEEP_REG);
606 /* For non-debuggable code, every definition of a lcl-var has
607 * to be checked to see if we need to open a new scope for it.
609 if (lclVarNum < compiler->lvaCount)
610 siCheckVarScope(lclVarNum, lclILoffs);
613 void CodeGen::genCodeForTreeFloat(GenTree* tree, RegSet::RegisterPreference* pref)
619 assert(tree->gtOper != GT_STMT);
621 // What kind of node do we have?
622 oper = tree->OperGet();
623 kind = tree->OperKind();
625 if (kind & GTK_CONST)
627 genFloatConst(tree, pref);
629 else if (kind & GTK_LEAF)
631 genFloatLeaf(tree, pref);
633 else if (kind & GTK_SMPOP)
635 genFloatSimple(tree, pref);
639 assert(oper == GT_CALL);
640 genCodeForCall(tree->AsCall(), true);
644 void CodeGen::genFloatLeaf(GenTree* tree, RegSet::RegisterPreference* pref)
646 regNumber reg = REG_NA;
648 switch (tree->OperGet())
651 // Does the variable live in a register?
653 if (!genMarkLclVar(tree))
658 noway_assert(tree->InReg());
659 reg = tree->gtRegVar.gtRegNum;
663 // We only use GT_LCL_FLD for lvAddrTaken vars, so we don't have
664 // to worry about it being enregistered.
665 noway_assert(compiler->lvaTable[tree->gtLclFld.gtLclNum].lvRegister == 0);
671 reg = regSet.PickRegFloat(tree->TypeGet(), pref);
672 genLoadFloat(tree, reg);
677 assert(!"unexpected leaf");
680 genCodeForTreeFloat_DONE(tree, reg);
684 void CodeGen::genLoadFloat(GenTree* tree, regNumber reg)
686 if (tree->IsRegVar())
688 // if it has been spilled, unspill it.%
689 LclVarDsc* varDsc = &compiler->lvaTable[tree->gtLclVarCommon.gtLclNum];
690 if (varDsc->lvSpilled)
692 UnspillFloat(varDsc);
695 inst_RV_RV(ins_FloatCopy(tree->TypeGet()), reg, tree->gtRegNum, tree->TypeGet());
699 bool unalignedLoad = false;
700 switch (tree->OperGet())
704 if (tree->gtFlags & GTF_IND_UNALIGNED)
705 unalignedLoad = true;
708 // Check for a misalignment on a Floating Point field
710 if (varTypeIsFloating(tree->TypeGet()))
712 if ((tree->gtLclFld.gtLclOffs % emitTypeSize(tree->TypeGet())) != 0)
714 unalignedLoad = true;
724 // Make the target addressable
726 regMaskTP addrReg = genMakeAddressable(tree, 0, RegSet::KEEP_REG, true);
727 regSet.rsLockUsedReg(addrReg); // Must prevent regSet.rsGrabReg from choosing an addrReg
729 var_types loadType = tree->TypeGet();
730 assert(loadType == TYP_DOUBLE || loadType == TYP_FLOAT);
732 // Unaligned Floating-Point Loads must be loaded into integer register(s)
733 // and then moved over to the Floating-Point register
734 regNumber intRegLo = regSet.rsGrabReg(RBM_ALLINT);
735 regNumber intRegHi = REG_NA;
736 regMaskTP tmpLockMask = genRegMask(intRegLo);
738 if (loadType == TYP_DOUBLE)
740 intRegHi = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(intRegLo));
741 tmpLockMask |= genRegMask(intRegHi);
744 regSet.rsLockReg(tmpLockMask); // Temporarily lock the intRegs
745 tree->gtType = TYP_INT; // Temporarily change the type to TYP_INT
747 inst_RV_TT(ins_Load(TYP_INT), intRegLo, tree);
748 regTracker.rsTrackRegTrash(intRegLo);
750 if (loadType == TYP_DOUBLE)
752 inst_RV_TT(ins_Load(TYP_INT), intRegHi, tree, 4);
753 regTracker.rsTrackRegTrash(intRegHi);
756 tree->gtType = loadType; // Change the type back to the floating point type
757 regSet.rsUnlockReg(tmpLockMask); // Unlock the intRegs
759 // move the integer register(s) over to the FP register
761 if (loadType == TYP_DOUBLE)
762 getEmitter()->emitIns_R_R_R(INS_vmov_i2d, EA_8BYTE, reg, intRegLo, intRegHi);
764 getEmitter()->emitIns_R_R(INS_vmov_i2f, EA_4BYTE, reg, intRegLo);
766 // Free up anything that was tied up by genMakeAddressable
768 regSet.rsUnlockUsedReg(addrReg);
769 genDoneAddressable(tree, addrReg, RegSet::KEEP_REG);
773 inst_RV_TT(ins_FloatLoad(tree->TypeGet()), reg, tree);
775 if (((tree->OperGet() == GT_CLS_VAR) || (tree->OperGet() == GT_IND)) && (tree->gtFlags & GTF_IND_VOLATILE))
777 // Emit a memory barrier instruction after the load
778 instGen_MemoryBarrier();
783 void CodeGen::genCodeForTreeFloat_DONE(GenTree* tree, regNumber reg)
785 return genCodeForTree_DONE(tree, reg);
788 void CodeGen::genFloatAsgArith(GenTree* tree)
790 // Set Flowgraph.cpp, line 13750
791 // arm VFP has tons of regs, 3-op instructions, and no addressing modes
792 // so asg ops are kind of pointless
793 noway_assert(!"Not Reachable for _TARGET_ARM_");
796 regNumber CodeGen::genAssignArithFloat(genTreeOps oper, GenTree* dst, regNumber dstreg, GenTree* src, regNumber srcreg)
800 // dst should be a regvar or memory
804 regNumber reg = dst->gtRegNum;
808 inst_RV_RV(ins_MathOp(oper, dst->gtType), reg, src->gtRegNum, dst->gtType);
812 inst_RV_TT(ins_MathOp(oper, dst->gtType), reg, src, 0, EmitSize(dst));
816 else // dst in memory
818 // since this is an asgop the ACTUAL destination is memory
819 // but it is also one of the sources and SSE ops do not allow mem dests
820 // so we have loaded it into a reg, and that is what dstreg represents
821 assert(dstreg != REG_NA);
825 inst_RV_RV(ins_MathOp(oper, dst->gtType), dstreg, src->gtRegNum, dst->gtType);
830 inst_RV_TT(ins_MathOp(oper, dst->gtType), dstreg, src, 0, EmitSize(dst));
833 dst->SetInReg(false); // ???
835 inst_TT_RV(ins_FloatStore(dst->gtType), dst, dstreg, 0, EmitSize(dst));
843 void CodeGen::genFloatArith(GenTree* tree, RegSet::RegisterPreference* tgtPref)
845 var_types type = tree->TypeGet();
846 genTreeOps oper = tree->OperGet();
847 GenTree* op1 = tree->gtGetOp1();
848 GenTree* op2 = tree->gtGetOp2IfPresent();
855 assert(oper == GT_ADD || oper == GT_SUB || oper == GT_MUL || oper == GT_DIV);
857 RegSet::RegisterPreference defaultPref(RBM_ALLFLOAT, RBM_NONE);
860 tgtPref = &defaultPref;
863 // Is the op2 (RHS)more complex than op1 (LHS)?
865 if (tree->gtFlags & GTF_REVERSE_OPS)
867 regMaskTP bestRegs = regSet.rsNarrowHint(RBM_ALLFLOAT, ~op1->gtRsvdRegs);
868 RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestRegs);
870 // Evaluate op2 into a floating point register
872 genCodeForTreeFloat(op2, &pref);
873 regSet.SetUsedRegFloat(op2, true);
875 // Evaluate op1 into any floating point register
877 genCodeForTreeFloat(op1);
878 regSet.SetUsedRegFloat(op1, true);
880 regNumber op1Reg = op1->gtRegNum;
881 regMaskTP op1Mask = genRegMaskFloat(op1Reg, type);
883 // Fix 388445 ARM JitStress WP7
884 regSet.rsLockUsedReg(op1Mask);
885 genRecoverReg(op2, RBM_ALLFLOAT, RegSet::KEEP_REG);
886 noway_assert(op2->InReg());
887 regSet.rsUnlockUsedReg(op1Mask);
889 regSet.SetUsedRegFloat(op1, false);
890 regSet.SetUsedRegFloat(op2, false);
894 regMaskTP bestRegs = regSet.rsNarrowHint(RBM_ALLFLOAT, ~op2->gtRsvdRegs);
895 RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestRegs);
897 // Evaluate op1 into a floating point register
899 genCodeForTreeFloat(op1, &pref);
900 regSet.SetUsedRegFloat(op1, true);
902 // Evaluate op2 into any floating point register
904 genCodeForTreeFloat(op2);
905 regSet.SetUsedRegFloat(op2, true);
907 regNumber op2Reg = op2->gtRegNum;
908 regMaskTP op2Mask = genRegMaskFloat(op2Reg, type);
910 // Fix 388445 ARM JitStress WP7
911 regSet.rsLockUsedReg(op2Mask);
912 genRecoverReg(op1, RBM_ALLFLOAT, RegSet::KEEP_REG);
913 noway_assert(op1->InReg());
914 regSet.rsUnlockUsedReg(op2Mask);
916 regSet.SetUsedRegFloat(op2, false);
917 regSet.SetUsedRegFloat(op1, false);
920 tgtReg = regSet.PickRegFloat(type, tgtPref, true);
922 noway_assert(op1->InReg());
923 noway_assert(op2->InReg());
925 inst_RV_RV_RV(ins_MathOp(oper, type), tgtReg, op1->gtRegNum, op2->gtRegNum, emitActualTypeSize(type));
927 genCodeForTreeFloat_DONE(tree, tgtReg);
930 regNumber CodeGen::genArithmFloat(
931 genTreeOps oper, GenTree* dst, regNumber dstreg, GenTree* src, regNumber srcreg, bool bReverse)
933 regNumber result = REG_NA;
935 assert(dstreg != REG_NA);
940 regNumber tempreg = srcreg;
947 if (srcreg == REG_NA)
951 inst_RV_RV(ins_MathOp(oper, dst->gtType), dst->gtRegNum, src->gtRegNum, dst->gtType);
955 inst_RV_TT(ins_MathOp(oper, dst->gtType), dst->gtRegNum, src);
960 inst_RV_RV(ins_MathOp(oper, dst->gtType), dstreg, srcreg, dst->gtType);
965 assert(result != REG_NA);
969 void CodeGen::genKeepAddressableFloat(GenTree* tree, regMaskTP* regMaskIntPtr, regMaskTP* regMaskFltPtr)
971 regMaskTP regMaskInt, regMaskFlt;
973 regMaskInt = *regMaskIntPtr;
974 regMaskFlt = *regMaskFltPtr;
976 *regMaskIntPtr = *regMaskFltPtr = 0;
978 switch (tree->OperGet())
981 // If register has been spilled, unspill it
982 if (tree->gtFlags & GTF_SPILLED)
984 UnspillFloat(&compiler->lvaTable[tree->gtLclVarCommon.gtLclNum]);
989 if (tree->gtFlags & GTF_SPILLED)
993 *regMaskFltPtr = genRegMaskFloat(tree->gtRegNum, tree->TypeGet());
1002 if (regMaskFlt == RBM_NONE)
1004 *regMaskIntPtr = genKeepAddressable(tree, regMaskInt, 0);
1012 if (tree->gtFlags & GTF_SPILLED)
1016 *regMaskFltPtr = genRegMaskFloat(tree->gtRegNum, tree->TypeGet());
1021 void CodeGen::genComputeAddressableFloat(GenTree* tree,
1022 regMaskTP addrRegInt,
1023 regMaskTP addrRegFlt,
1024 RegSet::KeepReg keptReg,
1026 RegSet::KeepReg keepReg,
1027 bool freeOnly /* = false */)
1029 noway_assert(genStillAddressable(tree));
1030 noway_assert(varTypeIsFloating(tree->TypeGet()));
1032 genDoneAddressableFloat(tree, addrRegInt, addrRegFlt, keptReg);
1037 reg = tree->gtRegNum;
1038 if (freeOnly && !(genRegMaskFloat(reg, tree->TypeGet()) & regSet.RegFreeFloat()))
1046 RegSet::RegisterPreference pref(needReg, RBM_NONE);
1047 reg = regSet.PickRegFloat(tree->TypeGet(), &pref);
1048 genLoadFloat(tree, reg);
1051 genMarkTreeInReg(tree, reg);
1053 if (keepReg == RegSet::KEEP_REG)
1055 regSet.SetUsedRegFloat(tree, true);
1059 void CodeGen::genDoneAddressableFloat(GenTree* tree,
1060 regMaskTP addrRegInt,
1061 regMaskTP addrRegFlt,
1062 RegSet::KeepReg keptReg)
1064 assert(!(addrRegInt && addrRegFlt));
1068 return genDoneAddressable(tree, addrRegInt, keptReg);
1070 else if (addrRegFlt)
1072 if (keptReg == RegSet::KEEP_REG)
1074 for (regNumber r = REG_FP_FIRST; r != REG_NA; r = regNextOfType(r, tree->TypeGet()))
1076 regMaskTP mask = genRegMaskFloat(r, tree->TypeGet());
1077 // some masks take up more than one bit
1078 if ((mask & addrRegFlt) == mask)
1080 regSet.SetUsedRegFloat(tree, false);
1087 GenTree* CodeGen::genMakeAddressableFloat(GenTree* tree,
1088 regMaskTP* regMaskIntPtr,
1089 regMaskTP* regMaskFltPtr,
1090 bool bCollapseConstantDoubles)
1092 *regMaskIntPtr = *regMaskFltPtr = 0;
1094 switch (tree->OperGet())
1098 genMarkLclVar(tree);
1107 // Try to make the address directly addressable
1109 if (genMakeIndAddrMode(tree->gtOp.gtOp1, tree, false, RBM_ALLFLOAT, RegSet::KEEP_REG, regMaskIntPtr, false))
1111 genUpdateLife(tree);
1116 GenTree* addr = tree;
1117 tree = tree->gtOp.gtOp1;
1118 genCodeForTree(tree, 0);
1119 regSet.rsMarkRegUsed(tree, addr);
1121 *regMaskIntPtr = genRegMask(tree->gtRegNum);
1128 genCodeForTreeFloat(tree);
1129 regSet.SetUsedRegFloat(tree, true);
1132 *regMaskFltPtr = genRegMaskFloat(tree->gtRegNum, tree->TypeGet());
1139 void CodeGen::genCodeForTreeCastFloat(GenTree* tree, RegSet::RegisterPreference* pref)
1141 GenTree* op1 = tree->gtOp.gtOp1;
1142 var_types from = op1->gtType;
1143 var_types to = tree->gtType;
1145 if (varTypeIsFloating(from))
1146 genCodeForTreeCastFromFloat(tree, pref);
1148 genCodeForTreeCastToFloat(tree, pref);
1151 void CodeGen::genCodeForTreeCastFromFloat(GenTree* tree, RegSet::RegisterPreference* pref)
1153 GenTree* op1 = tree->gtOp.gtOp1;
1154 var_types from = op1->gtType;
1155 var_types final = tree->gtType;
1156 var_types intermediate = tree->CastToType();
1161 assert(varTypeIsFloating(from));
1163 // Evaluate op1 into a floating point register
1165 if (varTypeIsFloating(final))
1167 genCodeForTreeFloat(op1, pref);
1171 RegSet::RegisterPreference defaultPref(RBM_ALLFLOAT, RBM_NONE);
1172 genCodeForTreeFloat(op1, &defaultPref);
1175 srcReg = op1->gtRegNum;
1177 if (varTypeIsFloating(final))
1179 // float => double or
1182 dstReg = regSet.PickRegFloat(final, pref);
1184 instruction ins = ins_FloatConv(final, from);
1185 if (!isMoveIns(ins) || (srcReg != dstReg))
1187 inst_RV_RV(ins, dstReg, srcReg, from);
1195 dstReg = regSet.rsPickReg(pref->ok, pref->best);
1197 RegSet::RegisterPreference defaultPref(RBM_ALLFLOAT, genRegMask(srcReg));
1198 regNumber intermediateReg = regSet.PickRegFloat(TYP_FLOAT, &defaultPref);
1200 if ((intermediate == TYP_UINT) && (final == TYP_INT))
1202 // Perform the conversion using the FP unit
1203 inst_RV_RV(ins_FloatConv(TYP_UINT, from), intermediateReg, srcReg, from);
1205 // Prevent the call to genIntegerCast
1210 // Perform the conversion using the FP unit
1211 inst_RV_RV(ins_FloatConv(TYP_INT, from), intermediateReg, srcReg, from);
1214 // the integer result is now in the FP register, move it to the integer ones
1215 getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, dstReg, intermediateReg);
1217 regTracker.rsTrackRegTrash(dstReg);
1219 // handle things like int <- short <- double
1220 if (final != intermediate)
1222 // lie about the register so integer cast logic will finish the job
1223 op1->gtRegNum = dstReg;
1224 genIntegerCast(tree, pref->ok, pref->best);
1229 genCodeForTree_DONE(tree, dstReg);
1232 void CodeGen::genCodeForTreeCastToFloat(GenTree* tree, RegSet::RegisterPreference* pref)
1240 GenTree* op1 = tree->gtOp.gtOp1;
1241 op1 = genCodeForCommaTree(op1); // Trim off any comma expressions.
1242 var_types from = op1->gtType;
1243 var_types to = tree->gtType;
1252 // load it into a register
1253 genCodeForTree(op1, 0);
1264 if (op1->gtOper == GT_LCL_FLD)
1266 genComputeReg(op1, 0, RegSet::ANY_REG, RegSet::FREE_REG);
1271 addrReg = genMakeAddressable(op1, 0, RegSet::FREE_REG);
1274 // Grab register for the cast
1275 dstReg = regSet.PickRegFloat(to, pref);
1277 // float type that is same size as the int we are coming from
1278 var_types vmovType = TYP_FLOAT;
1279 regNumber vmovReg = regSet.PickRegFloat(vmovType);
1281 if (tree->gtFlags & GTF_UNSIGNED)
1284 // Is the value a constant, or now sitting in a register?
1285 if (op1->InReg() || op1->IsCnsIntOrI())
1287 if (op1->IsCnsIntOrI())
1289 srcReg = genGetRegSetToIcon(op1->AsIntConCommon()->IconValue(), RBM_NONE, op1->TypeGet());
1293 srcReg = op1->gtRegNum;
1296 // move the integer register value over to the FP register
1297 getEmitter()->emitIns_R_R(INS_vmov_i2f, EA_4BYTE, vmovReg, srcReg);
1298 // now perform the conversion to the proper floating point representation
1299 inst_RV_RV(ins_FloatConv(to, from), dstReg, vmovReg, to);
1303 // Load the value from its address
1304 inst_RV_TT(ins_FloatLoad(vmovType), vmovReg, op1);
1305 inst_RV_RV(ins_FloatConv(to, from), dstReg, vmovReg, to);
1310 genDoneAddressable(op1, addrReg, RegSet::FREE_REG);
1312 genMarkTreeInReg(tree, dstReg);
1319 // This is a cast from float to double or double to float
1321 genCodeForTreeFloat(op1, pref);
1323 // Grab register for the cast
1324 dstReg = regSet.PickRegFloat(to, pref);
1326 if ((from != to) || (dstReg != op1->gtRegNum))
1328 inst_RV_RV(ins_FloatConv(to, from), dstReg, op1->gtRegNum, to);
1331 // Assign reg to tree
1332 genMarkTreeInReg(tree, dstReg);
1338 assert(!"unsupported cast");
1344 void CodeGen::genRoundFloatExpression(GenTree* op, var_types type)
1346 // Do nothing with memory resident opcodes - these are the right precision
1347 if (type == TYP_UNDEF)
1348 type = op->TypeGet();
1360 if (type == op->TypeGet())
1370 regMaskTP CodeGenInterface::genStressLockedMaskFloat()
1377 /*********************************************************************
1378 * Preserve used callee trashed registers across calls.
1381 void CodeGen::SpillForCallRegisterFP(regMaskTP noSpillMask)
1383 regMaskTP regBit = 1;
1384 for (regNumber regNum = REG_FIRST; regNum < REG_COUNT; regNum = REG_NEXT(regNum), regBit <<= 1)
1386 if (!(regBit & noSpillMask) && (regBit & RBM_FLT_CALLEE_TRASH) && regSet.rsUsedTree[regNum])
1388 SpillFloat(regNum, true);
1393 /*********************************************************************
1395 * Spill the used floating point register or the enregistered var.
1396 * If spilling for a call, then record so, so we can unspill the
1397 * ones that were spilled for the call.
1400 void CodeGenInterface::SpillFloat(regNumber reg, bool bIsCall /* = false */)
1402 regSet.rsSpillReg(reg);
1405 void CodeGen::UnspillFloatMachineDep(RegSet::SpillDsc* spillDsc)
1407 // Do actual unspill
1409 if (spillDsc->bEnregisteredVariable)
1411 NYI("unspill enreg var");
1412 reg = regSet.PickRegFloat();
1416 UnspillFloatMachineDep(spillDsc, false);
1420 void CodeGen::UnspillFloatMachineDep(RegSet::SpillDsc* spillDsc, bool useSameReg)
1422 assert(!spillDsc->bEnregisteredVariable);
1424 assert(spillDsc->spillTree->gtFlags & GTF_SPILLED);
1426 spillDsc->spillTree->gtFlags &= ~GTF_SPILLED;
1428 var_types type = spillDsc->spillTree->TypeGet();
1432 // Give register preference as the same register that the tree was originally using.
1433 reg = spillDsc->spillTree->gtRegNum;
1435 regMaskTP maskPref = genRegMask(reg);
1436 if (type == TYP_DOUBLE)
1438 assert((maskPref & RBM_DBL_REGS) != 0);
1439 maskPref |= genRegMask(REG_NEXT(reg));
1442 RegSet::RegisterPreference pref(RBM_ALLFLOAT, maskPref);
1443 reg = regSet.PickRegFloat(type, &pref);
1447 reg = regSet.PickRegFloat();
1450 // load from spilled spot
1451 compiler->codeGen->reloadFloatReg(type, spillDsc->spillTemp, reg);
1453 compiler->codeGen->genMarkTreeInReg(spillDsc->spillTree, reg);
1454 regSet.SetUsedRegFloat(spillDsc->spillTree, true);
1458 instruction genFloatJumpInstr(genTreeOps cmp, bool isUnordered)
1467 return isUnordered ? INS_blt : INS_blo;
1469 return isUnordered ? INS_ble : INS_bls;
1471 return isUnordered ? INS_bpl : INS_bge;
1473 return isUnordered ? INS_bhi : INS_bgt;
1479 void CodeGen::genCondJumpFloat(GenTree* cond, BasicBlock* jumpTrue, BasicBlock* jumpFalse)
1481 assert(jumpTrue && jumpFalse);
1482 assert(!(cond->gtFlags & GTF_REVERSE_OPS)); // Done in genCondJump()
1483 assert(varTypeIsFloating(cond->gtOp.gtOp1->gtType));
1485 GenTree* op1 = cond->gtOp.gtOp1;
1486 GenTree* op2 = cond->gtOp.gtOp2;
1487 genTreeOps cmp = cond->OperGet();
1488 bool isUnordered = cond->gtFlags & GTF_RELOP_NAN_UN ? true : false;
1490 regMaskTP bestRegs = regSet.rsNarrowHint(RBM_ALLFLOAT, ~op2->gtRsvdRegs);
1491 RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestRegs);
1493 // Prepare operands.
1494 genCodeForTreeFloat(op1, &pref);
1495 regSet.SetUsedRegFloat(op1, true);
1497 genCodeForTreeFloat(op2);
1498 regSet.SetUsedRegFloat(op2, true);
1500 genRecoverReg(op1, RBM_ALLFLOAT, RegSet::KEEP_REG);
1501 noway_assert(op1->InReg());
1504 getEmitter()->emitIns_R_R(INS_vcmp, EmitSize(op1), op1->gtRegNum, op2->gtRegNum);
1506 // vmrs with register 0xf has special meaning of transferring flags
1507 getEmitter()->emitIns_R(INS_vmrs, EA_4BYTE, REG_R15);
1509 regSet.SetUsedRegFloat(op2, false);
1510 regSet.SetUsedRegFloat(op1, false);
1512 getEmitter()->emitIns_J(genFloatJumpInstr(cmp, isUnordered), jumpTrue);
1515 #endif // LEGACY_BACKEND