Fix reading Time zone rules using Julian days (#17672)
[platform/upstream/coreclr.git] / src / jit / registerfp.cpp
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.
4
5 #include "jitpch.h"
6 #ifdef _MSC_VER
7 #pragma hdrstop
8 #endif
9
10 #ifdef LEGACY_BACKEND // This file is NOT used for the RyuJIT backend that uses the linear scan register allocator.
11
12 #include "compiler.h"
13 #include "emit.h"
14 #include "codegen.h"
15
16 #ifndef _TARGET_ARM_
17 #error "Non-ARM target for registerfp.cpp"
18 #endif // !_TARGET_ARM_
19
20 // get the next argument register which is aligned to 'alignment' # of bytes
21 regNumber alignFloatArgReg(regNumber argReg, int alignment)
22 {
23     assert(isValidFloatArgReg(argReg));
24
25     int regsize_alignment = alignment /= REGSIZE_BYTES;
26     if (genMapFloatRegNumToRegArgNum(argReg) % regsize_alignment)
27         argReg = genRegArgNext(argReg);
28
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));
32
33     return argReg;
34 }
35
36 // Instruction list
37 // N=normal, R=reverse, P=pop
38
39 void CodeGen::genFloatConst(GenTree* tree, RegSet::RegisterPreference* pref)
40 {
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;
45
46     regNumber dst = regSet.PickRegFloat(type, pref);
47
48     if (type == TYP_FLOAT)
49     {
50         regNumber reg = regSet.rsPickReg();
51
52         float f = forceCastToFloat(constValue);
53         genSetRegToIcon(reg, *((int*)(&f)));
54         getEmitter()->emitIns_R_R(INS_vmov_i2f, EA_4BYTE, dst, reg);
55     }
56     else
57     {
58         assert(type == TYP_DOUBLE);
59         regNumber reg1 = regSet.rsPickReg();
60         regNumber reg2 = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(reg1));
61
62         genSetRegToIcon(reg1, cv[0]);
63         regSet.rsLockReg(genRegMask(reg1));
64         genSetRegToIcon(reg2, cv[1]);
65         regSet.rsUnlockReg(genRegMask(reg1));
66
67         getEmitter()->emitIns_R_R_R(INS_vmov_i2d, EA_8BYTE, dst, reg1, reg2);
68     }
69     genMarkTreeInReg(tree, dst);
70
71     return;
72 }
73
74 void CodeGen::genFloatMath(GenTree* tree, RegSet::RegisterPreference* pref)
75 {
76     assert(tree->OperGet() == GT_INTRINSIC);
77
78     GenTree* op1 = tree->gtOp.gtOp1;
79
80     // get tree into a register
81     genCodeForTreeFloat(op1, pref);
82
83     instruction ins;
84
85     switch (tree->gtIntrinsic.gtIntrinsicId)
86     {
87         case CORINFO_INTRINSIC_Sin:
88             ins = INS_invalid;
89             break;
90         case CORINFO_INTRINSIC_Cos:
91             ins = INS_invalid;
92             break;
93         case CORINFO_INTRINSIC_Sqrt:
94             ins = INS_vsqrt;
95             break;
96         case CORINFO_INTRINSIC_Abs:
97             ins = INS_vabs;
98             break;
99         case CORINFO_INTRINSIC_Round:
100         {
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);
107             return;
108         }
109         break;
110         default:
111             unreached();
112     }
113
114     if (ins != INS_invalid)
115     {
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);
121     }
122     else
123     {
124         unreached();
125         // If unreached is removed, mark register that holds tree
126         // genCodeForTreeFloat_DONE(tree, op1->gtRegNum);
127     }
128
129     return;
130 }
131
132 void CodeGen::genFloatSimple(GenTree* tree, RegSet::RegisterPreference* pref)
133 {
134     assert(tree->OperKind() & GTK_SMPOP);
135     var_types type = tree->TypeGet();
136
137     RegSet::RegisterPreference defaultPref(RBM_ALLFLOAT, RBM_NONE);
138     if (pref == NULL)
139     {
140         pref = &defaultPref;
141     }
142
143     switch (tree->OperGet())
144     {
145         // Assignment
146         case GT_ASG:
147         {
148             genFloatAssign(tree);
149             break;
150         }
151
152         // Arithmetic binops
153         case GT_ADD:
154         case GT_SUB:
155         case GT_MUL:
156         case GT_DIV:
157         {
158             genFloatArith(tree, pref);
159             break;
160         }
161
162         case GT_NEG:
163         {
164             GenTree* op1 = tree->gtOp.gtOp1;
165
166             // get the tree into a register
167             genCodeForTreeFloat(op1, pref);
168
169             // change the sign
170             regNumber reg = regSet.PickRegFloat(type, pref);
171             genMarkTreeInReg(tree, reg);
172             inst_RV_RV(ins_MathOp(tree->OperGet(), type), reg, op1->gtRegNum, type);
173
174             // mark register that holds tree
175             genCodeForTreeFloat_DONE(tree, reg);
176             return;
177         }
178
179         case GT_IND:
180         {
181             regMaskTP addrReg;
182
183             // Make sure the address value is 'addressable' */
184             addrReg = genMakeAddressable(tree, 0, RegSet::FREE_REG);
185
186             // Load the value onto the FP stack
187             regNumber reg = regSet.PickRegFloat(type, pref);
188             genLoadFloat(tree, reg);
189
190             genDoneAddressable(tree, addrReg, RegSet::FREE_REG);
191
192             genCodeForTreeFloat_DONE(tree, reg);
193
194             break;
195         }
196         case GT_CAST:
197         {
198             genCodeForTreeCastFloat(tree, pref);
199             break;
200         }
201
202         // Asg-Arithmetic ops
203         case GT_ASG_ADD:
204         case GT_ASG_SUB:
205         case GT_ASG_MUL:
206         case GT_ASG_DIV:
207         {
208             genFloatAsgArith(tree);
209             break;
210         }
211         case GT_INTRINSIC:
212             genFloatMath(tree, pref);
213             break;
214
215         case GT_RETURN:
216         {
217             GenTree* op1 = tree->gtOp.gtOp1;
218             assert(op1);
219
220             pref->best = (type == TYP_DOUBLE) ? RBM_DOUBLERET : RBM_FLOATRET;
221
222             // Compute the result
223             genCodeForTreeFloat(op1, pref);
224
225             inst_RV_TT(ins_FloatConv(tree->TypeGet(), op1->TypeGet()), REG_FLOATRET, op1);
226             if (compiler->info.compIsVarArgs || compiler->opts.compUseSoftFP)
227             {
228                 if (tree->TypeGet() == TYP_FLOAT)
229                 {
230                     inst_RV_RV(INS_vmov_f2i, REG_INTRET, REG_FLOATRET, TYP_FLOAT, EA_4BYTE);
231                 }
232                 else
233                 {
234                     assert(tree->TypeGet() == TYP_DOUBLE);
235                     inst_RV_RV_RV(INS_vmov_d2i, REG_INTRET, REG_NEXT(REG_INTRET), REG_FLOATRET, EA_8BYTE);
236                 }
237             }
238             break;
239         }
240         case GT_ARGPLACE:
241             break;
242
243         case GT_COMMA:
244         {
245             GenTree* op1 = tree->gtOp.gtOp1;
246             GenTree* op2 = tree->gtGetOp2IfPresent();
247
248             if (tree->gtFlags & GTF_REVERSE_OPS)
249             {
250                 genCodeForTreeFloat(op2, pref);
251
252                 regSet.SetUsedRegFloat(op2, true);
253                 genEvalSideEffects(op1);
254                 regSet.SetUsedRegFloat(op2, false);
255             }
256             else
257             {
258                 genEvalSideEffects(op1);
259                 genCodeForTreeFloat(op2, pref);
260             }
261
262             genCodeForTreeFloat_DONE(tree, op2->gtRegNum);
263             break;
264         }
265
266         case GT_CKFINITE:
267             genFloatCheckFinite(tree, pref);
268             break;
269
270         default:
271             NYI("Unhandled register FP codegen");
272     }
273 }
274
275 // generate code for ckfinite tree/instruction
276 void CodeGen::genFloatCheckFinite(GenTree* tree, RegSet::RegisterPreference* pref)
277 {
278     TempDsc* temp;
279     int      offs;
280
281     GenTree* op1 = tree->gtOp.gtOp1;
282
283     // Offset of the DWord containing the exponent
284     offs = (op1->gtType == TYP_FLOAT) ? 0 : sizeof(int);
285
286     // get tree into a register
287     genCodeForTreeFloat(op1, pref);
288
289     regNumber reg = regSet.rsPickReg();
290
291     int expMask;
292     if (op1->gtType == TYP_FLOAT)
293     {
294         getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, reg, op1->gtRegNum);
295         expMask = 0x7F800000;
296     }
297     else // double
298     {
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;
303     }
304     regTracker.rsTrackRegTrash(reg);
305
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);
309
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);
313
314     genCodeForTreeFloat_DONE(tree, op1->gtRegNum);
315 }
316
317 void CodeGen::genFloatAssign(GenTree* tree)
318 {
319     var_types type = tree->TypeGet();
320     GenTree*  op1  = tree->gtGetOp1();
321     GenTree*  op2  = tree->gtGetOp2IfPresent();
322
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;
328
329     unsigned lclVarNum = compiler->lvaCount;
330     unsigned lclILoffs = DUMMY_INIT(0);
331
332     noway_assert(tree->OperGet() == GT_ASG);
333
334     // Is the target a floating-point local variable?
335     //  possibly even an enregistered floating-point local variable?
336     //
337     switch (op1->gtOper)
338     {
339         unsigned   varNum;
340         LclVarDsc* varDsc;
341
342         case GT_LCL_FLD:
343             // Check for a misalignment on a Floating Point field
344             //
345             if (varTypeIsFloating(op1->TypeGet()))
346             {
347                 if ((op1->gtLclFld.gtLclOffs % emitTypeSize(op1->TypeGet())) != 0)
348                 {
349                     unaligned = true;
350                 }
351             }
352             break;
353
354         case GT_LCL_VAR:
355             varNum = op1->gtLclVarCommon.gtLclNum;
356             noway_assert(varNum < compiler->lvaCount);
357             varDsc = compiler->lvaTable + varNum;
358
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.
363             //
364             if (compiler->opts.compScopeInfo && !compiler->opts.compDbgCode && (compiler->info.compVarScopesCount > 0))
365             {
366                 lclVarNum = varNum;
367                 lclILoffs = op1->gtLclVar.gtLclILoffs;
368             }
369
370             // Dead Store assert (with min opts we may have dead stores)
371             //
372             noway_assert(!varDsc->lvTracked || compiler->opts.MinOpts() || !(op1->gtFlags & GTF_VAR_DEATH));
373
374             // Does this variable live in a register?
375             //
376             if (genMarkLclVar(op1))
377             {
378                 noway_assert(!compiler->opts.compDbgCode); // We don't enregister any floats with debug codegen
379
380                 // Get hold of the target register
381                 //
382                 regNumber op1Reg = op1->gtRegVar.gtRegNum;
383
384                 // the variable being assigned should be dead in op2
385                 assert(!varDsc->lvTracked ||
386                        !VarSetOps::IsMember(compiler, genUpdateLiveSetForward(op2), varDsc->lvVarIndex));
387
388                 // Setup register preferencing, so that we try to target the op1 enregistered variable
389                 //
390                 regMaskTP bestMask = genRegMask(op1Reg);
391                 if (type == TYP_DOUBLE)
392                 {
393                     assert((bestMask & RBM_DBL_REGS) != 0);
394                     bestMask |= genRegMask(REG_NEXT(op1Reg));
395                 }
396                 RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestMask);
397
398                 // Evaluate op2 into a floating point register
399                 //
400                 genCodeForTreeFloat(op2, &pref);
401
402                 noway_assert(op2->InReg());
403
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
407                 //
408                 if ((op2->gtRegNum != op1Reg) || (op2->TypeGet() != type))
409                 {
410                     regMaskTP spillRegs = regSet.rsMaskUsed & genRegMaskFloat(op1Reg, op1->TypeGet());
411                     if (spillRegs != 0)
412                         regSet.rsSpillRegs(spillRegs);
413
414                     assert(type == op1->TypeGet());
415
416                     inst_RV_RV(ins_FloatConv(type, op2->TypeGet()), op1Reg, op2->gtRegNum, type);
417                 }
418                 genUpdateLife(op1);
419                 goto DONE_ASG;
420             }
421             break;
422
423         case GT_CLS_VAR:
424         case GT_IND:
425             // Check for a volatile/unaligned store
426             //
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)
430                 volat = true;
431             if (op1->gtFlags & GTF_IND_UNALIGNED)
432                 unaligned = true;
433             break;
434
435         default:
436             break;
437     }
438
439     // Is the value being assigned an enregistered floating-point local variable?
440     //
441     switch (op2->gtOper)
442     {
443         case GT_LCL_VAR:
444
445             if (!genMarkLclVar(op2))
446                 break;
447
448             __fallthrough;
449
450         case GT_REG_VAR:
451
452             // We must honor the order evalauation in case op1 reassigns our op2 register
453             //
454             if (tree->gtFlags & GTF_REVERSE_OPS)
455                 break;
456
457             // Is there an implicit conversion that we have to insert?
458             // Handle this case with the normal cases below.
459             //
460             if (type != op2->TypeGet())
461                 break;
462
463             // Make the target addressable
464             //
465             addrReg = genMakeAddressable(op1, needRegOp1, RegSet::KEEP_REG, true);
466
467             noway_assert(op2->InReg());
468             noway_assert(op2->IsRegVar());
469
470             op2reg = op2->gtRegVar.gtRegNum;
471             genUpdateLife(op2);
472
473             goto CHK_VOLAT_UNALIGN;
474         default:
475             break;
476     }
477
478     // Is the op2 (RHS) more complex than op1 (LHS)?
479     //
480     if (tree->gtFlags & GTF_REVERSE_OPS)
481     {
482         regMaskTP                  bestRegs = regSet.rsNarrowHint(RBM_ALLFLOAT, ~op1->gtRsvdRegs);
483         RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestRegs);
484
485         // Generate op2 (RHS) into a floating point register
486         //
487         genCodeForTreeFloat(op2, &pref);
488         regSet.SetUsedRegFloat(op2, true);
489
490         // Make the target addressable
491         //
492         addrReg = genMakeAddressable(op1, needRegOp1, RegSet::KEEP_REG, true);
493
494         genRecoverReg(op2, RBM_ALLFLOAT, RegSet::KEEP_REG);
495         noway_assert(op2->InReg());
496         regSet.SetUsedRegFloat(op2, false);
497     }
498     else
499     {
500         needRegOp1 = regSet.rsNarrowHint(needRegOp1, ~op2->gtRsvdRegs);
501
502         // Make the target addressable
503         //
504         addrReg = genMakeAddressable(op1, needRegOp1, RegSet::KEEP_REG, true);
505
506         // Generate the RHS into any floating point register
507         genCodeForTreeFloat(op2);
508     }
509     noway_assert(op2->InReg());
510
511     op2reg = op2->gtRegNum;
512
513     // Is there an implicit conversion that we have to insert?
514     //
515     if (type != op2->TypeGet())
516     {
517         regMaskTP bestMask = genRegMask(op2reg);
518         if (type == TYP_DOUBLE)
519         {
520             if (bestMask & RBM_DBL_REGS)
521             {
522                 bestMask |= genRegMask(REG_NEXT(op2reg));
523             }
524             else
525             {
526                 bestMask |= genRegMask(REG_PREV(op2reg));
527             }
528         }
529         RegSet::RegisterPreference op2Pref(RBM_ALLFLOAT, bestMask);
530         op2reg = regSet.PickRegFloat(type, &op2Pref);
531
532         inst_RV_RV(ins_FloatConv(type, op2->TypeGet()), op2reg, op2->gtRegNum, type);
533     }
534
535     // Make sure the LHS is still addressable
536     //
537     addrReg = genKeepAddressable(op1, addrReg);
538
539 CHK_VOLAT_UNALIGN:
540
541     regSet.rsLockUsedReg(addrReg); // Must prevent unaligned regSet.rsGrabReg from choosing an addrReg
542
543     if (volat)
544     {
545         // Emit a memory barrier instruction before the store
546         instGen_MemoryBarrier();
547     }
548     if (unaligned)
549     {
550         var_types storeType = op1->TypeGet();
551         assert(storeType == TYP_DOUBLE || storeType == TYP_FLOAT);
552
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);
557
558         if (storeType == TYP_DOUBLE)
559         {
560             intRegHi = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(intRegLo));
561             tmpLockMask |= genRegMask(intRegHi);
562         }
563
564         // move the FP register over to the integer register(s)
565         //
566         if (storeType == TYP_DOUBLE)
567         {
568             getEmitter()->emitIns_R_R_R(INS_vmov_d2i, EA_8BYTE, intRegLo, intRegHi, op2reg);
569             regTracker.rsTrackRegTrash(intRegHi);
570         }
571         else
572         {
573             getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, intRegLo, op2reg);
574         }
575         regTracker.rsTrackRegTrash(intRegLo);
576
577         regSet.rsLockReg(tmpLockMask); // Temporarily lock the intRegs
578         op1->gtType = TYP_INT;         // Temporarily change the type to TYP_INT
579
580         inst_TT_RV(ins_Store(TYP_INT), op1, intRegLo);
581
582         if (storeType == TYP_DOUBLE)
583         {
584             inst_TT_RV(ins_Store(TYP_INT), op1, intRegHi, 4);
585         }
586
587         op1->gtType = storeType;         // Change the type back to the floating point type
588         regSet.rsUnlockReg(tmpLockMask); // Unlock the intRegs
589     }
590     else
591     {
592         // Move the value into the target
593         //
594         inst_TT_RV(ins_Store(op1->TypeGet()), op1, op2reg);
595     }
596
597     // Free up anything that was tied up by the LHS
598     //
599     regSet.rsUnlockUsedReg(addrReg);
600     genDoneAddressable(op1, addrReg, RegSet::KEEP_REG);
601
602 DONE_ASG:
603
604     genUpdateLife(tree);
605
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.
608      */
609     if (lclVarNum < compiler->lvaCount)
610         siCheckVarScope(lclVarNum, lclILoffs);
611 }
612
613 void CodeGen::genCodeForTreeFloat(GenTree* tree, RegSet::RegisterPreference* pref)
614 {
615     genTreeOps oper;
616     unsigned   kind;
617
618     assert(tree);
619     assert(tree->gtOper != GT_STMT);
620
621     // What kind of node do we have?
622     oper = tree->OperGet();
623     kind = tree->OperKind();
624
625     if (kind & GTK_CONST)
626     {
627         genFloatConst(tree, pref);
628     }
629     else if (kind & GTK_LEAF)
630     {
631         genFloatLeaf(tree, pref);
632     }
633     else if (kind & GTK_SMPOP)
634     {
635         genFloatSimple(tree, pref);
636     }
637     else
638     {
639         assert(oper == GT_CALL);
640         genCodeForCall(tree->AsCall(), true);
641     }
642 }
643
644 void CodeGen::genFloatLeaf(GenTree* tree, RegSet::RegisterPreference* pref)
645 {
646     regNumber reg = REG_NA;
647
648     switch (tree->OperGet())
649     {
650         case GT_LCL_VAR:
651             // Does the variable live in a register?
652             //
653             if (!genMarkLclVar(tree))
654                 goto MEM_LEAF;
655             __fallthrough;
656
657         case GT_REG_VAR:
658             noway_assert(tree->InReg());
659             reg = tree->gtRegVar.gtRegNum;
660             break;
661
662         case GT_LCL_FLD:
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);
666             __fallthrough;
667
668         case GT_CLS_VAR:
669
670         MEM_LEAF:
671             reg = regSet.PickRegFloat(tree->TypeGet(), pref);
672             genLoadFloat(tree, reg);
673             break;
674
675         default:
676             DISPTREE(tree);
677             assert(!"unexpected leaf");
678     }
679
680     genCodeForTreeFloat_DONE(tree, reg);
681     return;
682 }
683
684 void CodeGen::genLoadFloat(GenTree* tree, regNumber reg)
685 {
686     if (tree->IsRegVar())
687     {
688         // if it has been spilled, unspill it.%
689         LclVarDsc* varDsc = &compiler->lvaTable[tree->gtLclVarCommon.gtLclNum];
690         if (varDsc->lvSpilled)
691         {
692             UnspillFloat(varDsc);
693         }
694
695         inst_RV_RV(ins_FloatCopy(tree->TypeGet()), reg, tree->gtRegNum, tree->TypeGet());
696     }
697     else
698     {
699         bool unalignedLoad = false;
700         switch (tree->OperGet())
701         {
702             case GT_IND:
703             case GT_CLS_VAR:
704                 if (tree->gtFlags & GTF_IND_UNALIGNED)
705                     unalignedLoad = true;
706                 break;
707             case GT_LCL_FLD:
708                 // Check for a misalignment on a Floating Point field
709                 //
710                 if (varTypeIsFloating(tree->TypeGet()))
711                 {
712                     if ((tree->gtLclFld.gtLclOffs % emitTypeSize(tree->TypeGet())) != 0)
713                     {
714                         unalignedLoad = true;
715                     }
716                 }
717                 break;
718             default:
719                 break;
720         }
721
722         if (unalignedLoad)
723         {
724             // Make the target addressable
725             //
726             regMaskTP addrReg = genMakeAddressable(tree, 0, RegSet::KEEP_REG, true);
727             regSet.rsLockUsedReg(addrReg); // Must prevent regSet.rsGrabReg from choosing an addrReg
728
729             var_types loadType = tree->TypeGet();
730             assert(loadType == TYP_DOUBLE || loadType == TYP_FLOAT);
731
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);
737
738             if (loadType == TYP_DOUBLE)
739             {
740                 intRegHi = regSet.rsGrabReg(RBM_ALLINT & ~genRegMask(intRegLo));
741                 tmpLockMask |= genRegMask(intRegHi);
742             }
743
744             regSet.rsLockReg(tmpLockMask); // Temporarily lock the intRegs
745             tree->gtType = TYP_INT;        // Temporarily change the type to TYP_INT
746
747             inst_RV_TT(ins_Load(TYP_INT), intRegLo, tree);
748             regTracker.rsTrackRegTrash(intRegLo);
749
750             if (loadType == TYP_DOUBLE)
751             {
752                 inst_RV_TT(ins_Load(TYP_INT), intRegHi, tree, 4);
753                 regTracker.rsTrackRegTrash(intRegHi);
754             }
755
756             tree->gtType = loadType;         // Change the type back to the floating point type
757             regSet.rsUnlockReg(tmpLockMask); // Unlock the intRegs
758
759             // move the integer register(s) over to the FP register
760             //
761             if (loadType == TYP_DOUBLE)
762                 getEmitter()->emitIns_R_R_R(INS_vmov_i2d, EA_8BYTE, reg, intRegLo, intRegHi);
763             else
764                 getEmitter()->emitIns_R_R(INS_vmov_i2f, EA_4BYTE, reg, intRegLo);
765
766             // Free up anything that was tied up by genMakeAddressable
767             //
768             regSet.rsUnlockUsedReg(addrReg);
769             genDoneAddressable(tree, addrReg, RegSet::KEEP_REG);
770         }
771         else
772         {
773             inst_RV_TT(ins_FloatLoad(tree->TypeGet()), reg, tree);
774         }
775         if (((tree->OperGet() == GT_CLS_VAR) || (tree->OperGet() == GT_IND)) && (tree->gtFlags & GTF_IND_VOLATILE))
776         {
777             // Emit a memory barrier instruction after the load
778             instGen_MemoryBarrier();
779         }
780     }
781 }
782
783 void CodeGen::genCodeForTreeFloat_DONE(GenTree* tree, regNumber reg)
784 {
785     return genCodeForTree_DONE(tree, reg);
786 }
787
788 void CodeGen::genFloatAsgArith(GenTree* tree)
789 {
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_");
794 }
795
796 regNumber CodeGen::genAssignArithFloat(genTreeOps oper, GenTree* dst, regNumber dstreg, GenTree* src, regNumber srcreg)
797 {
798     regNumber result;
799
800     // dst should be a regvar or memory
801
802     if (dst->IsRegVar())
803     {
804         regNumber reg = dst->gtRegNum;
805
806         if (src->IsRegVar())
807         {
808             inst_RV_RV(ins_MathOp(oper, dst->gtType), reg, src->gtRegNum, dst->gtType);
809         }
810         else
811         {
812             inst_RV_TT(ins_MathOp(oper, dst->gtType), reg, src, 0, EmitSize(dst));
813         }
814         result = reg;
815     }
816     else // dst in memory
817     {
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);
822
823         if ((src->InReg()))
824         {
825             inst_RV_RV(ins_MathOp(oper, dst->gtType), dstreg, src->gtRegNum, dst->gtType);
826         }
827         else
828         {
829             // mem mem operation
830             inst_RV_TT(ins_MathOp(oper, dst->gtType), dstreg, src, 0, EmitSize(dst));
831         }
832
833         dst->SetInReg(false); // ???
834
835         inst_TT_RV(ins_FloatStore(dst->gtType), dst, dstreg, 0, EmitSize(dst));
836
837         result = REG_NA;
838     }
839
840     return result;
841 }
842
843 void CodeGen::genFloatArith(GenTree* tree, RegSet::RegisterPreference* tgtPref)
844 {
845     var_types  type = tree->TypeGet();
846     genTreeOps oper = tree->OperGet();
847     GenTree*   op1  = tree->gtGetOp1();
848     GenTree*   op2  = tree->gtGetOp2IfPresent();
849
850     regNumber  tgtReg;
851     unsigned   varNum;
852     LclVarDsc* varDsc;
853     VARSET_TP  varBit;
854
855     assert(oper == GT_ADD || oper == GT_SUB || oper == GT_MUL || oper == GT_DIV);
856
857     RegSet::RegisterPreference defaultPref(RBM_ALLFLOAT, RBM_NONE);
858     if (tgtPref == NULL)
859     {
860         tgtPref = &defaultPref;
861     }
862
863     // Is the op2 (RHS)more complex than op1 (LHS)?
864     //
865     if (tree->gtFlags & GTF_REVERSE_OPS)
866     {
867         regMaskTP                  bestRegs = regSet.rsNarrowHint(RBM_ALLFLOAT, ~op1->gtRsvdRegs);
868         RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestRegs);
869
870         // Evaluate op2 into a floating point register
871         //
872         genCodeForTreeFloat(op2, &pref);
873         regSet.SetUsedRegFloat(op2, true);
874
875         // Evaluate op1 into any floating point register
876         //
877         genCodeForTreeFloat(op1);
878         regSet.SetUsedRegFloat(op1, true);
879
880         regNumber op1Reg  = op1->gtRegNum;
881         regMaskTP op1Mask = genRegMaskFloat(op1Reg, type);
882
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);
888
889         regSet.SetUsedRegFloat(op1, false);
890         regSet.SetUsedRegFloat(op2, false);
891     }
892     else
893     {
894         regMaskTP                  bestRegs = regSet.rsNarrowHint(RBM_ALLFLOAT, ~op2->gtRsvdRegs);
895         RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestRegs);
896
897         // Evaluate op1 into a floating point register
898         //
899         genCodeForTreeFloat(op1, &pref);
900         regSet.SetUsedRegFloat(op1, true);
901
902         // Evaluate op2 into any floating point register
903         //
904         genCodeForTreeFloat(op2);
905         regSet.SetUsedRegFloat(op2, true);
906
907         regNumber op2Reg  = op2->gtRegNum;
908         regMaskTP op2Mask = genRegMaskFloat(op2Reg, type);
909
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);
915
916         regSet.SetUsedRegFloat(op2, false);
917         regSet.SetUsedRegFloat(op1, false);
918     }
919
920     tgtReg = regSet.PickRegFloat(type, tgtPref, true);
921
922     noway_assert(op1->InReg());
923     noway_assert(op2->InReg());
924
925     inst_RV_RV_RV(ins_MathOp(oper, type), tgtReg, op1->gtRegNum, op2->gtRegNum, emitActualTypeSize(type));
926
927     genCodeForTreeFloat_DONE(tree, tgtReg);
928 }
929
930 regNumber CodeGen::genArithmFloat(
931     genTreeOps oper, GenTree* dst, regNumber dstreg, GenTree* src, regNumber srcreg, bool bReverse)
932 {
933     regNumber result = REG_NA;
934
935     assert(dstreg != REG_NA);
936
937     if (bReverse)
938     {
939         GenTree*  temp    = src;
940         regNumber tempreg = srcreg;
941         src               = dst;
942         srcreg            = dstreg;
943         dst               = temp;
944         dstreg            = tempreg;
945     }
946
947     if (srcreg == REG_NA)
948     {
949         if (src->IsRegVar())
950         {
951             inst_RV_RV(ins_MathOp(oper, dst->gtType), dst->gtRegNum, src->gtRegNum, dst->gtType);
952         }
953         else
954         {
955             inst_RV_TT(ins_MathOp(oper, dst->gtType), dst->gtRegNum, src);
956         }
957     }
958     else
959     {
960         inst_RV_RV(ins_MathOp(oper, dst->gtType), dstreg, srcreg, dst->gtType);
961     }
962
963     result = dstreg;
964
965     assert(result != REG_NA);
966     return result;
967 }
968
969 void CodeGen::genKeepAddressableFloat(GenTree* tree, regMaskTP* regMaskIntPtr, regMaskTP* regMaskFltPtr)
970 {
971     regMaskTP regMaskInt, regMaskFlt;
972
973     regMaskInt = *regMaskIntPtr;
974     regMaskFlt = *regMaskFltPtr;
975
976     *regMaskIntPtr = *regMaskFltPtr = 0;
977
978     switch (tree->OperGet())
979     {
980         case GT_REG_VAR:
981             // If register has been spilled, unspill it
982             if (tree->gtFlags & GTF_SPILLED)
983             {
984                 UnspillFloat(&compiler->lvaTable[tree->gtLclVarCommon.gtLclNum]);
985             }
986             break;
987
988         case GT_CNS_DBL:
989             if (tree->gtFlags & GTF_SPILLED)
990             {
991                 UnspillFloat(tree);
992             }
993             *regMaskFltPtr = genRegMaskFloat(tree->gtRegNum, tree->TypeGet());
994             break;
995
996         case GT_LCL_FLD:
997         case GT_LCL_VAR:
998         case GT_CLS_VAR:
999             break;
1000
1001         case GT_IND:
1002             if (regMaskFlt == RBM_NONE)
1003             {
1004                 *regMaskIntPtr = genKeepAddressable(tree, regMaskInt, 0);
1005                 *regMaskFltPtr = 0;
1006                 return;
1007             }
1008             __fallthrough;
1009
1010         default:
1011             *regMaskIntPtr = 0;
1012             if (tree->gtFlags & GTF_SPILLED)
1013             {
1014                 UnspillFloat(tree);
1015             }
1016             *regMaskFltPtr = genRegMaskFloat(tree->gtRegNum, tree->TypeGet());
1017             break;
1018     }
1019 }
1020
1021 void CodeGen::genComputeAddressableFloat(GenTree*        tree,
1022                                          regMaskTP       addrRegInt,
1023                                          regMaskTP       addrRegFlt,
1024                                          RegSet::KeepReg keptReg,
1025                                          regMaskTP       needReg,
1026                                          RegSet::KeepReg keepReg,
1027                                          bool            freeOnly /* = false */)
1028 {
1029     noway_assert(genStillAddressable(tree));
1030     noway_assert(varTypeIsFloating(tree->TypeGet()));
1031
1032     genDoneAddressableFloat(tree, addrRegInt, addrRegFlt, keptReg);
1033
1034     regNumber reg;
1035     if (tree->InReg())
1036     {
1037         reg = tree->gtRegNum;
1038         if (freeOnly && !(genRegMaskFloat(reg, tree->TypeGet()) & regSet.RegFreeFloat()))
1039         {
1040             goto LOAD_REG;
1041         }
1042     }
1043     else
1044     {
1045     LOAD_REG:
1046         RegSet::RegisterPreference pref(needReg, RBM_NONE);
1047         reg = regSet.PickRegFloat(tree->TypeGet(), &pref);
1048         genLoadFloat(tree, reg);
1049     }
1050
1051     genMarkTreeInReg(tree, reg);
1052
1053     if (keepReg == RegSet::KEEP_REG)
1054     {
1055         regSet.SetUsedRegFloat(tree, true);
1056     }
1057 }
1058
1059 void CodeGen::genDoneAddressableFloat(GenTree*        tree,
1060                                       regMaskTP       addrRegInt,
1061                                       regMaskTP       addrRegFlt,
1062                                       RegSet::KeepReg keptReg)
1063 {
1064     assert(!(addrRegInt && addrRegFlt));
1065
1066     if (addrRegInt)
1067     {
1068         return genDoneAddressable(tree, addrRegInt, keptReg);
1069     }
1070     else if (addrRegFlt)
1071     {
1072         if (keptReg == RegSet::KEEP_REG)
1073         {
1074             for (regNumber r = REG_FP_FIRST; r != REG_NA; r = regNextOfType(r, tree->TypeGet()))
1075             {
1076                 regMaskTP mask = genRegMaskFloat(r, tree->TypeGet());
1077                 // some masks take up more than one bit
1078                 if ((mask & addrRegFlt) == mask)
1079                 {
1080                     regSet.SetUsedRegFloat(tree, false);
1081                 }
1082             }
1083         }
1084     }
1085 }
1086
1087 GenTree* CodeGen::genMakeAddressableFloat(GenTree*   tree,
1088                                           regMaskTP* regMaskIntPtr,
1089                                           regMaskTP* regMaskFltPtr,
1090                                           bool       bCollapseConstantDoubles)
1091 {
1092     *regMaskIntPtr = *regMaskFltPtr = 0;
1093
1094     switch (tree->OperGet())
1095     {
1096
1097         case GT_LCL_VAR:
1098             genMarkLclVar(tree);
1099             __fallthrough;
1100
1101         case GT_REG_VAR:
1102         case GT_LCL_FLD:
1103         case GT_CLS_VAR:
1104             return tree;
1105
1106         case GT_IND:
1107             // Try to make the address directly addressable
1108
1109             if (genMakeIndAddrMode(tree->gtOp.gtOp1, tree, false, RBM_ALLFLOAT, RegSet::KEEP_REG, regMaskIntPtr, false))
1110             {
1111                 genUpdateLife(tree);
1112                 return tree;
1113             }
1114             else
1115             {
1116                 GenTree* addr = tree;
1117                 tree          = tree->gtOp.gtOp1;
1118                 genCodeForTree(tree, 0);
1119                 regSet.rsMarkRegUsed(tree, addr);
1120
1121                 *regMaskIntPtr = genRegMask(tree->gtRegNum);
1122                 return addr;
1123             }
1124
1125         // fall through
1126
1127         default:
1128             genCodeForTreeFloat(tree);
1129             regSet.SetUsedRegFloat(tree, true);
1130
1131             // update mask
1132             *regMaskFltPtr = genRegMaskFloat(tree->gtRegNum, tree->TypeGet());
1133
1134             return tree;
1135             break;
1136     }
1137 }
1138
1139 void CodeGen::genCodeForTreeCastFloat(GenTree* tree, RegSet::RegisterPreference* pref)
1140 {
1141     GenTree*  op1  = tree->gtOp.gtOp1;
1142     var_types from = op1->gtType;
1143     var_types to   = tree->gtType;
1144
1145     if (varTypeIsFloating(from))
1146         genCodeForTreeCastFromFloat(tree, pref);
1147     else
1148         genCodeForTreeCastToFloat(tree, pref);
1149 }
1150
1151 void CodeGen::genCodeForTreeCastFromFloat(GenTree* tree, RegSet::RegisterPreference* pref)
1152 {
1153     GenTree*  op1          = tree->gtOp.gtOp1;
1154     var_types from         = op1->gtType;
1155     var_types final        = tree->gtType;
1156     var_types intermediate = tree->CastToType();
1157
1158     regNumber srcReg;
1159     regNumber dstReg;
1160
1161     assert(varTypeIsFloating(from));
1162
1163     // Evaluate op1 into a floating point register
1164     //
1165     if (varTypeIsFloating(final))
1166     {
1167         genCodeForTreeFloat(op1, pref);
1168     }
1169     else
1170     {
1171         RegSet::RegisterPreference defaultPref(RBM_ALLFLOAT, RBM_NONE);
1172         genCodeForTreeFloat(op1, &defaultPref);
1173     }
1174
1175     srcReg = op1->gtRegNum;
1176
1177     if (varTypeIsFloating(final))
1178     {
1179         // float  => double  or
1180         // double => float
1181
1182         dstReg = regSet.PickRegFloat(final, pref);
1183
1184         instruction ins = ins_FloatConv(final, from);
1185         if (!isMoveIns(ins) || (srcReg != dstReg))
1186         {
1187             inst_RV_RV(ins, dstReg, srcReg, from);
1188         }
1189     }
1190     else
1191     {
1192         // float  => int  or
1193         // double => int
1194
1195         dstReg = regSet.rsPickReg(pref->ok, pref->best);
1196
1197         RegSet::RegisterPreference defaultPref(RBM_ALLFLOAT, genRegMask(srcReg));
1198         regNumber                  intermediateReg = regSet.PickRegFloat(TYP_FLOAT, &defaultPref);
1199
1200         if ((intermediate == TYP_UINT) && (final == TYP_INT))
1201         {
1202             // Perform the conversion using the FP unit
1203             inst_RV_RV(ins_FloatConv(TYP_UINT, from), intermediateReg, srcReg, from);
1204
1205             // Prevent the call to genIntegerCast
1206             final = TYP_UINT;
1207         }
1208         else
1209         {
1210             // Perform the conversion using the FP unit
1211             inst_RV_RV(ins_FloatConv(TYP_INT, from), intermediateReg, srcReg, from);
1212         }
1213
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);
1216
1217         regTracker.rsTrackRegTrash(dstReg);
1218
1219         // handle things like int <- short <- double
1220         if (final != intermediate)
1221         {
1222             // lie about the register so integer cast logic will finish the job
1223             op1->gtRegNum = dstReg;
1224             genIntegerCast(tree, pref->ok, pref->best);
1225         }
1226     }
1227
1228     genUpdateLife(op1);
1229     genCodeForTree_DONE(tree, dstReg);
1230 }
1231
1232 void CodeGen::genCodeForTreeCastToFloat(GenTree* tree, RegSet::RegisterPreference* pref)
1233 {
1234     regNumber srcReg;
1235     regNumber dstReg;
1236     regNumber vmovReg;
1237
1238     regMaskTP addrReg;
1239
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;
1244
1245     switch (from)
1246     {
1247         case TYP_BOOL:
1248         case TYP_BYTE:
1249         case TYP_UBYTE:
1250         case TYP_USHORT:
1251         case TYP_SHORT:
1252             // load it into a register
1253             genCodeForTree(op1, 0);
1254
1255             __fallthrough;
1256
1257         case TYP_BYREF:
1258             from = TYP_INT;
1259
1260             __fallthrough;
1261
1262         case TYP_INT:
1263         {
1264             if (op1->gtOper == GT_LCL_FLD)
1265             {
1266                 genComputeReg(op1, 0, RegSet::ANY_REG, RegSet::FREE_REG);
1267                 addrReg = 0;
1268             }
1269             else
1270             {
1271                 addrReg = genMakeAddressable(op1, 0, RegSet::FREE_REG);
1272             }
1273
1274             // Grab register for the cast
1275             dstReg = regSet.PickRegFloat(to, pref);
1276
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);
1280
1281             if (tree->gtFlags & GTF_UNSIGNED)
1282                 from = TYP_UINT;
1283
1284             // Is the value a constant, or now sitting in a register?
1285             if (op1->InReg() || op1->IsCnsIntOrI())
1286             {
1287                 if (op1->IsCnsIntOrI())
1288                 {
1289                     srcReg = genGetRegSetToIcon(op1->AsIntConCommon()->IconValue(), RBM_NONE, op1->TypeGet());
1290                 }
1291                 else
1292                 {
1293                     srcReg = op1->gtRegNum;
1294                 }
1295
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);
1300             }
1301             else
1302             {
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);
1306             }
1307
1308             if (addrReg)
1309             {
1310                 genDoneAddressable(op1, addrReg, RegSet::FREE_REG);
1311             }
1312             genMarkTreeInReg(tree, dstReg);
1313
1314             break;
1315         }
1316         case TYP_FLOAT:
1317         case TYP_DOUBLE:
1318         {
1319             //  This is a cast from float to double or double to float
1320
1321             genCodeForTreeFloat(op1, pref);
1322
1323             // Grab register for the cast
1324             dstReg = regSet.PickRegFloat(to, pref);
1325
1326             if ((from != to) || (dstReg != op1->gtRegNum))
1327             {
1328                 inst_RV_RV(ins_FloatConv(to, from), dstReg, op1->gtRegNum, to);
1329             }
1330
1331             // Assign reg to tree
1332             genMarkTreeInReg(tree, dstReg);
1333
1334             break;
1335         }
1336         default:
1337         {
1338             assert(!"unsupported cast");
1339             break;
1340         }
1341     }
1342 }
1343
1344 void CodeGen::genRoundFloatExpression(GenTree* op, var_types type)
1345 {
1346     // Do nothing with memory resident opcodes - these are the right precision
1347     if (type == TYP_UNDEF)
1348         type = op->TypeGet();
1349
1350     switch (op->gtOper)
1351     {
1352         case GT_LCL_VAR:
1353             genMarkLclVar(op);
1354             __fallthrough;
1355
1356         case GT_LCL_FLD:
1357         case GT_CLS_VAR:
1358         case GT_CNS_DBL:
1359         case GT_IND:
1360             if (type == op->TypeGet())
1361                 return;
1362
1363         default:
1364             break;
1365     }
1366 }
1367
1368 #ifdef DEBUG
1369
1370 regMaskTP CodeGenInterface::genStressLockedMaskFloat()
1371 {
1372     return 0;
1373 }
1374
1375 #endif // DEBUG
1376
1377 /*********************************************************************
1378  * Preserve used callee trashed registers across calls.
1379  *
1380  */
1381 void CodeGen::SpillForCallRegisterFP(regMaskTP noSpillMask)
1382 {
1383     regMaskTP regBit = 1;
1384     for (regNumber regNum = REG_FIRST; regNum < REG_COUNT; regNum = REG_NEXT(regNum), regBit <<= 1)
1385     {
1386         if (!(regBit & noSpillMask) && (regBit & RBM_FLT_CALLEE_TRASH) && regSet.rsUsedTree[regNum])
1387         {
1388             SpillFloat(regNum, true);
1389         }
1390     }
1391 }
1392
1393 /*********************************************************************
1394  *
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.
1398  *
1399  */
1400 void CodeGenInterface::SpillFloat(regNumber reg, bool bIsCall /* = false */)
1401 {
1402     regSet.rsSpillReg(reg);
1403 }
1404
1405 void CodeGen::UnspillFloatMachineDep(RegSet::SpillDsc* spillDsc)
1406 {
1407     // Do actual unspill
1408     regNumber reg;
1409     if (spillDsc->bEnregisteredVariable)
1410     {
1411         NYI("unspill enreg var");
1412         reg = regSet.PickRegFloat();
1413     }
1414     else
1415     {
1416         UnspillFloatMachineDep(spillDsc, false);
1417     }
1418 }
1419
1420 void CodeGen::UnspillFloatMachineDep(RegSet::SpillDsc* spillDsc, bool useSameReg)
1421 {
1422     assert(!spillDsc->bEnregisteredVariable);
1423
1424     assert(spillDsc->spillTree->gtFlags & GTF_SPILLED);
1425
1426     spillDsc->spillTree->gtFlags &= ~GTF_SPILLED;
1427
1428     var_types type = spillDsc->spillTree->TypeGet();
1429     regNumber reg;
1430     if (useSameReg)
1431     {
1432         // Give register preference as the same register that the tree was originally using.
1433         reg = spillDsc->spillTree->gtRegNum;
1434
1435         regMaskTP maskPref = genRegMask(reg);
1436         if (type == TYP_DOUBLE)
1437         {
1438             assert((maskPref & RBM_DBL_REGS) != 0);
1439             maskPref |= genRegMask(REG_NEXT(reg));
1440         }
1441
1442         RegSet::RegisterPreference pref(RBM_ALLFLOAT, maskPref);
1443         reg = regSet.PickRegFloat(type, &pref);
1444     }
1445     else
1446     {
1447         reg = regSet.PickRegFloat();
1448     }
1449
1450     // load from spilled spot
1451     compiler->codeGen->reloadFloatReg(type, spillDsc->spillTemp, reg);
1452
1453     compiler->codeGen->genMarkTreeInReg(spillDsc->spillTree, reg);
1454     regSet.SetUsedRegFloat(spillDsc->spillTree, true);
1455 }
1456
1457 //
1458 instruction genFloatJumpInstr(genTreeOps cmp, bool isUnordered)
1459 {
1460     switch (cmp)
1461     {
1462         case GT_EQ:
1463             return INS_beq;
1464         case GT_NE:
1465             return INS_bne;
1466         case GT_LT:
1467             return isUnordered ? INS_blt : INS_blo;
1468         case GT_LE:
1469             return isUnordered ? INS_ble : INS_bls;
1470         case GT_GE:
1471             return isUnordered ? INS_bpl : INS_bge;
1472         case GT_GT:
1473             return isUnordered ? INS_bhi : INS_bgt;
1474         default:
1475             unreached();
1476     }
1477 }
1478
1479 void CodeGen::genCondJumpFloat(GenTree* cond, BasicBlock* jumpTrue, BasicBlock* jumpFalse)
1480 {
1481     assert(jumpTrue && jumpFalse);
1482     assert(!(cond->gtFlags & GTF_REVERSE_OPS)); // Done in genCondJump()
1483     assert(varTypeIsFloating(cond->gtOp.gtOp1->gtType));
1484
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;
1489
1490     regMaskTP                  bestRegs = regSet.rsNarrowHint(RBM_ALLFLOAT, ~op2->gtRsvdRegs);
1491     RegSet::RegisterPreference pref(RBM_ALLFLOAT, bestRegs);
1492
1493     // Prepare operands.
1494     genCodeForTreeFloat(op1, &pref);
1495     regSet.SetUsedRegFloat(op1, true);
1496
1497     genCodeForTreeFloat(op2);
1498     regSet.SetUsedRegFloat(op2, true);
1499
1500     genRecoverReg(op1, RBM_ALLFLOAT, RegSet::KEEP_REG);
1501     noway_assert(op1->InReg());
1502
1503     // cmp here
1504     getEmitter()->emitIns_R_R(INS_vcmp, EmitSize(op1), op1->gtRegNum, op2->gtRegNum);
1505
1506     // vmrs with register 0xf has special meaning of transferring flags
1507     getEmitter()->emitIns_R(INS_vmrs, EA_4BYTE, REG_R15);
1508
1509     regSet.SetUsedRegFloat(op2, false);
1510     regSet.SetUsedRegFloat(op1, false);
1511
1512     getEmitter()->emitIns_J(genFloatJumpInstr(cmp, isUnordered), jumpTrue);
1513 }
1514
1515 #endif // LEGACY_BACKEND