RyuJIT/ARM32: Update GT_PUTARG_REG codegen.
[platform/upstream/coreclr.git] / src / jit / codegenarm.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 /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
7 XX                                                                           XX
8 XX                        ARM Code Generator                                 XX
9 XX                                                                           XX
10 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
11 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
12 */
13 #include "jitpch.h"
14 #ifdef _MSC_VER
15 #pragma hdrstop
16 #endif
17
18 #ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator
19
20 #ifdef _TARGET_ARM_
21 #include "codegen.h"
22 #include "lower.h"
23 #include "gcinfo.h"
24 #include "emit.h"
25
26 //------------------------------------------------------------------------
27 // genCallFinally: Generate a call to the finally block.
28 //
29 BasicBlock* CodeGen::genCallFinally(BasicBlock* block)
30 {
31     BasicBlock* bbFinallyRet = nullptr;
32
33     // We don't have retless calls, since we use the BBJ_ALWAYS to point at a NOP pad where
34     // we would have otherwise created retless calls.
35     assert(block->isBBCallAlwaysPair());
36
37     assert(block->bbNext != NULL);
38     assert(block->bbNext->bbJumpKind == BBJ_ALWAYS);
39     assert(block->bbNext->bbJumpDest != NULL);
40     assert(block->bbNext->bbJumpDest->bbFlags & BBF_FINALLY_TARGET);
41
42     bbFinallyRet = block->bbNext->bbJumpDest;
43     bbFinallyRet->bbFlags |= BBF_JMP_TARGET;
44
45     // Load the address where the finally funclet should return into LR.
46     // The funclet prolog/epilog will do "push {lr}" / "pop {pc}" to do the return.
47     getEmitter()->emitIns_R_L(INS_movw, EA_4BYTE_DSP_RELOC, bbFinallyRet, REG_LR);
48     getEmitter()->emitIns_R_L(INS_movt, EA_4BYTE_DSP_RELOC, bbFinallyRet, REG_LR);
49
50     // Jump to the finally BB
51     inst_JMP(EJ_jmp, block->bbJumpDest);
52
53     // The BBJ_ALWAYS is used because the BBJ_CALLFINALLY can't point to the
54     // jump target using bbJumpDest - that is already used to point
55     // to the finally block. So just skip past the BBJ_ALWAYS unless the
56     // block is RETLESS.
57     assert(!(block->bbFlags & BBF_RETLESS_CALL));
58     assert(block->isBBCallAlwaysPair());
59     return block->bbNext;
60 }
61
62 //------------------------------------------------------------------------
63 // genEHCatchRet:
64 void CodeGen::genEHCatchRet(BasicBlock* block)
65 {
66     getEmitter()->emitIns_R_L(INS_movw, EA_4BYTE_DSP_RELOC, block->bbJumpDest, REG_INTRET);
67     getEmitter()->emitIns_R_L(INS_movt, EA_4BYTE_DSP_RELOC, block->bbJumpDest, REG_INTRET);
68 }
69
70 //------------------------------------------------------------------------
71 // instGen_Set_Reg_To_Imm: Move an immediate value into an integer register.
72 //
73 void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size, regNumber reg, ssize_t imm, insFlags flags)
74 {
75     // reg cannot be a FP register
76     assert(!genIsValidFloatReg(reg));
77
78     if (!compiler->opts.compReloc)
79     {
80         size = EA_SIZE(size); // Strip any Reloc flags from size if we aren't doing relocs
81     }
82
83     if (EA_IS_RELOC(size))
84     {
85         getEmitter()->emitIns_R_I(INS_movw, size, reg, imm);
86         getEmitter()->emitIns_R_I(INS_movt, size, reg, imm);
87     }
88     else if (imm == 0)
89     {
90         instGen_Set_Reg_To_Zero(size, reg, flags);
91     }
92     else
93     {
94         if (arm_Valid_Imm_For_Mov(imm))
95         {
96             getEmitter()->emitIns_R_I(INS_mov, size, reg, imm, flags);
97         }
98         else // We have to use a movw/movt pair of instructions
99         {
100             ssize_t imm_lo16 = (imm & 0xffff);
101             ssize_t imm_hi16 = (imm >> 16) & 0xffff;
102
103             assert(arm_Valid_Imm_For_Mov(imm_lo16));
104             assert(imm_hi16 != 0);
105
106             getEmitter()->emitIns_R_I(INS_movw, size, reg, imm_lo16);
107
108             // If we've got a low register, the high word is all bits set,
109             // and the high bit of the low word is set, we can sign extend
110             // halfword and save two bytes of encoding. This can happen for
111             // small magnitude negative numbers 'n' for -32768 <= n <= -1.
112
113             if (getEmitter()->isLowRegister(reg) && (imm_hi16 == 0xffff) && ((imm_lo16 & 0x8000) == 0x8000))
114             {
115                 getEmitter()->emitIns_R_R(INS_sxth, EA_2BYTE, reg, reg);
116             }
117             else
118             {
119                 getEmitter()->emitIns_R_I(INS_movt, size, reg, imm_hi16);
120             }
121
122             if (flags == INS_FLAGS_SET)
123                 getEmitter()->emitIns_R_R(INS_mov, size, reg, reg, INS_FLAGS_SET);
124         }
125     }
126
127     regTracker.rsTrackRegIntCns(reg, imm);
128 }
129
130 //------------------------------------------------------------------------
131 // genSetRegToConst: Generate code to set a register 'targetReg' of type 'targetType'
132 //    to the constant specified by the constant (GT_CNS_INT or GT_CNS_DBL) in 'tree'.
133 //
134 // Notes:
135 //    This does not call genProduceReg() on the target register.
136 //
137 void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTreePtr tree)
138 {
139     switch (tree->gtOper)
140     {
141         case GT_CNS_INT:
142         {
143             // relocatable values tend to come down as a CNS_INT of native int type
144             // so the line between these two opcodes is kind of blurry
145             GenTreeIntConCommon* con    = tree->AsIntConCommon();
146             ssize_t              cnsVal = con->IconValue();
147
148             bool needReloc = compiler->opts.compReloc && tree->IsIconHandle();
149             if (needReloc)
150             {
151                 instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, targetReg, cnsVal);
152                 regTracker.rsTrackRegTrash(targetReg);
153             }
154             else
155             {
156                 genSetRegToIcon(targetReg, cnsVal, targetType);
157             }
158         }
159         break;
160
161         case GT_CNS_DBL:
162         {
163             GenTreeDblCon* dblConst   = tree->AsDblCon();
164             double         constValue = dblConst->gtDblCon.gtDconVal;
165             // TODO-ARM-CQ: Do we have a faster/smaller way to generate 0.0 in thumb2 ISA ?
166             if (targetType == TYP_FLOAT)
167             {
168                 // Get a temp integer register
169                 regNumber tmpReg = tree->GetSingleTempReg();
170
171                 float f = forceCastToFloat(constValue);
172                 genSetRegToIcon(tmpReg, *((int*)(&f)));
173                 getEmitter()->emitIns_R_R(INS_vmov_i2f, EA_4BYTE, targetReg, tmpReg);
174             }
175             else
176             {
177                 assert(targetType == TYP_DOUBLE);
178
179                 unsigned* cv = (unsigned*)&constValue;
180
181                 // Get two temp integer registers
182                 regNumber tmpReg1 = tree->ExtractTempReg();
183                 regNumber tmpReg2 = tree->GetSingleTempReg();
184
185                 genSetRegToIcon(tmpReg1, cv[0]);
186                 genSetRegToIcon(tmpReg2, cv[1]);
187
188                 getEmitter()->emitIns_R_R_R(INS_vmov_i2d, EA_8BYTE, targetReg, tmpReg1, tmpReg2);
189             }
190         }
191         break;
192
193         default:
194             unreached();
195     }
196 }
197
198 //------------------------------------------------------------------------
199 // genCodeForBinary: Generate code for many binary arithmetic operators
200 // This method is expected to have called genConsumeOperands() before calling it.
201 //
202 // Arguments:
203 //    treeNode - The binary operation for which we are generating code.
204 //
205 // Return Value:
206 //    None.
207 //
208 // Notes:
209 //    Mul and div are not handled here.
210 //    See the assert below for the operators that are handled.
211
212 void CodeGen::genCodeForBinary(GenTree* treeNode)
213 {
214     const genTreeOps oper       = treeNode->OperGet();
215     regNumber        targetReg  = treeNode->gtRegNum;
216     var_types        targetType = treeNode->TypeGet();
217     emitter*         emit       = getEmitter();
218
219     assert(oper == GT_ADD || oper == GT_SUB || oper == GT_MUL || oper == GT_ADD_LO || oper == GT_ADD_HI ||
220            oper == GT_SUB_LO || oper == GT_SUB_HI || oper == GT_OR || oper == GT_XOR || oper == GT_AND);
221
222     GenTreePtr op1 = treeNode->gtGetOp1();
223     GenTreePtr op2 = treeNode->gtGetOp2();
224
225     instruction ins = genGetInsForOper(oper, targetType);
226
227     // The arithmetic node must be sitting in a register (since it's not contained)
228     noway_assert(targetReg != REG_NA);
229
230     if ((oper == GT_ADD_LO || oper == GT_SUB_LO))
231     {
232         // During decomposition, all operands become reg
233         assert(!op1->isContained() && !op2->isContained());
234         emit->emitIns_R_R_R(ins, emitTypeSize(treeNode), treeNode->gtRegNum, op1->gtRegNum, op2->gtRegNum,
235                             INS_FLAGS_SET);
236     }
237     else
238     {
239         regNumber r = emit->emitInsTernary(ins, emitTypeSize(treeNode), treeNode, op1, op2);
240         assert(r == targetReg);
241     }
242
243     genProduceReg(treeNode);
244 }
245
246 //------------------------------------------------------------------------
247 // genReturn: Generates code for return statement.
248 //            In case of struct return, delegates to the genStructReturn method.
249 //
250 // Arguments:
251 //    treeNode - The GT_RETURN or GT_RETFILT tree node.
252 //
253 // Return Value:
254 //    None
255 //
256 void CodeGen::genReturn(GenTreePtr treeNode)
257 {
258     assert(treeNode->OperGet() == GT_RETURN || treeNode->OperGet() == GT_RETFILT);
259     GenTreePtr op1        = treeNode->gtGetOp1();
260     var_types  targetType = treeNode->TypeGet();
261
262 #ifdef DEBUG
263     if (targetType == TYP_VOID)
264     {
265         assert(op1 == nullptr);
266     }
267 #endif
268
269     if (treeNode->TypeGet() == TYP_LONG)
270     {
271         assert(op1 != nullptr);
272         noway_assert(op1->OperGet() == GT_LONG);
273         GenTree* loRetVal = op1->gtGetOp1();
274         GenTree* hiRetVal = op1->gtGetOp2();
275         noway_assert((loRetVal->gtRegNum != REG_NA) && (hiRetVal->gtRegNum != REG_NA));
276
277         genConsumeReg(loRetVal);
278         genConsumeReg(hiRetVal);
279         if (loRetVal->gtRegNum != REG_LNGRET_LO)
280         {
281             inst_RV_RV(ins_Copy(targetType), REG_LNGRET_LO, loRetVal->gtRegNum, TYP_INT);
282         }
283         if (hiRetVal->gtRegNum != REG_LNGRET_HI)
284         {
285             inst_RV_RV(ins_Copy(targetType), REG_LNGRET_HI, hiRetVal->gtRegNum, TYP_INT);
286         }
287     }
288     else
289     {
290         if (varTypeIsStruct(treeNode))
291         {
292             NYI_ARM("struct return");
293         }
294         else if (targetType != TYP_VOID)
295         {
296             assert(op1 != nullptr);
297             noway_assert(op1->gtRegNum != REG_NA);
298
299             // !! NOTE !! genConsumeReg will clear op1 as GC ref after it has
300             // consumed a reg for the operand. This is because the variable
301             // is dead after return. But we are issuing more instructions
302             // like "profiler leave callback" after this consumption. So
303             // if you are issuing more instructions after this point,
304             // remember to keep the variable live up until the new method
305             // exit point where it is actually dead.
306             genConsumeReg(op1);
307
308             regNumber retReg = varTypeIsFloating(treeNode) ? REG_FLOATRET : REG_INTRET;
309             if (op1->gtRegNum != retReg)
310             {
311                 inst_RV_RV(ins_Move_Extend(targetType, true), retReg, op1->gtRegNum, targetType);
312             }
313         }
314     }
315 }
316
317 //------------------------------------------------------------------------
318 // genCodeForTreeNode Generate code for a single node in the tree.
319 //
320 // Preconditions:
321 //    All operands have been evaluated.
322 //
323 void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
324 {
325     regNumber targetReg  = treeNode->gtRegNum;
326     var_types targetType = treeNode->TypeGet();
327     emitter*  emit       = getEmitter();
328
329 #ifdef DEBUG
330     lastConsumedNode = nullptr;
331     if (compiler->verbose)
332     {
333         unsigned seqNum = treeNode->gtSeqNum; // Useful for setting a conditional break in Visual Studio
334         compiler->gtDispLIRNode(treeNode, "Generating: ");
335     }
336 #endif
337
338     // contained nodes are part of their parents for codegen purposes
339     // ex : immediates, most LEAs
340     if (treeNode->isContained())
341     {
342         return;
343     }
344
345     switch (treeNode->gtOper)
346     {
347         case GT_LCLHEAP:
348             genLclHeap(treeNode);
349             break;
350
351         case GT_CNS_INT:
352         case GT_CNS_DBL:
353             genSetRegToConst(targetReg, targetType, treeNode);
354             genProduceReg(treeNode);
355             break;
356
357         case GT_NOT:
358             assert(!varTypeIsFloating(targetType));
359
360             __fallthrough;
361
362         case GT_NEG:
363         {
364             instruction ins = genGetInsForOper(treeNode->OperGet(), targetType);
365
366             // The arithmetic node must be sitting in a register (since it's not contained)
367             assert(!treeNode->isContained());
368             // The dst can only be a register.
369             assert(targetReg != REG_NA);
370
371             GenTreePtr operand = treeNode->gtGetOp1();
372             assert(!operand->isContained());
373             // The src must be a register.
374             regNumber operandReg = genConsumeReg(operand);
375
376             if (ins == INS_vneg)
377             {
378                 getEmitter()->emitIns_R_R(ins, emitTypeSize(treeNode), targetReg, operandReg);
379             }
380             else
381             {
382                 getEmitter()->emitIns_R_R_I(ins, emitTypeSize(treeNode), targetReg, operandReg, 0);
383             }
384         }
385             genProduceReg(treeNode);
386             break;
387
388         case GT_OR:
389         case GT_XOR:
390         case GT_AND:
391             assert(varTypeIsIntegralOrI(treeNode));
392             __fallthrough;
393
394         case GT_ADD_LO:
395         case GT_ADD_HI:
396         case GT_SUB_LO:
397         case GT_SUB_HI:
398         case GT_ADD:
399         case GT_SUB:
400         case GT_MUL:
401             genConsumeOperands(treeNode->AsOp());
402             genCodeForBinary(treeNode);
403             break;
404
405         case GT_LSH:
406         case GT_RSH:
407         case GT_RSZ:
408         case GT_ROR:
409             genCodeForShift(treeNode);
410             break;
411
412         case GT_LSH_HI:
413         case GT_RSH_LO:
414             genCodeForShiftLong(treeNode);
415             break;
416
417         case GT_CAST:
418             // Cast is never contained (?)
419             noway_assert(targetReg != REG_NA);
420
421             if (varTypeIsFloating(targetType) && varTypeIsFloating(treeNode->gtOp.gtOp1))
422             {
423                 // Casts float/double <--> double/float
424                 genFloatToFloatCast(treeNode);
425             }
426             else if (varTypeIsFloating(treeNode->gtOp.gtOp1))
427             {
428                 // Casts float/double --> int32/int64
429                 genFloatToIntCast(treeNode);
430             }
431             else if (varTypeIsFloating(targetType))
432             {
433                 // Casts int32/uint32/int64/uint64 --> float/double
434                 genIntToFloatCast(treeNode);
435             }
436             else
437             {
438                 // Casts int <--> int
439                 genIntToIntCast(treeNode);
440             }
441             // The per-case functions call genProduceReg()
442             break;
443
444         case GT_LCL_VAR:
445         {
446             GenTreeLclVarCommon* lcl = treeNode->AsLclVarCommon();
447             // lcl_vars are not defs
448             assert((treeNode->gtFlags & GTF_VAR_DEF) == 0);
449
450             bool isRegCandidate = compiler->lvaTable[lcl->gtLclNum].lvIsRegCandidate();
451
452             if (isRegCandidate && !(treeNode->gtFlags & GTF_VAR_DEATH))
453             {
454                 assert((treeNode->InReg()) || (treeNode->gtFlags & GTF_SPILLED));
455             }
456
457             // If this is a register candidate that has been spilled, genConsumeReg() will
458             // reload it at the point of use.  Otherwise, if it's not in a register, we load it here.
459
460             if (!treeNode->InReg() && !(treeNode->gtFlags & GTF_SPILLED))
461             {
462                 assert(!isRegCandidate);
463                 emit->emitIns_R_S(ins_Load(treeNode->TypeGet()), emitTypeSize(treeNode), treeNode->gtRegNum,
464                                   lcl->gtLclNum, 0);
465                 genProduceReg(treeNode);
466             }
467         }
468         break;
469
470         case GT_LCL_FLD_ADDR:
471         case GT_LCL_VAR_ADDR:
472         {
473             // Address of a local var.  This by itself should never be allocated a register.
474             // If it is worth storing the address in a register then it should be cse'ed into
475             // a temp and that would be allocated a register.
476             noway_assert(targetType == TYP_BYREF);
477             noway_assert(!treeNode->InReg());
478
479             inst_RV_TT(INS_lea, targetReg, treeNode, 0, EA_BYREF);
480         }
481             genProduceReg(treeNode);
482             break;
483
484         case GT_LCL_FLD:
485         {
486             NYI_IF(targetType == TYP_STRUCT, "GT_LCL_FLD: struct load local field not supported");
487             NYI_IF(treeNode->gtRegNum == REG_NA, "GT_LCL_FLD: load local field not into a register is not supported");
488
489             emitAttr size   = emitTypeSize(targetType);
490             unsigned offs   = treeNode->gtLclFld.gtLclOffs;
491             unsigned varNum = treeNode->gtLclVarCommon.gtLclNum;
492             assert(varNum < compiler->lvaCount);
493
494             if (varTypeIsFloating(targetType))
495             {
496                 if (treeNode->InReg())
497                 {
498                     NYI("GT_LCL_FLD with reg-to-reg floating point move");
499                 }
500                 else
501                 {
502                     emit->emitIns_R_S(ins_Load(targetType), size, targetReg, varNum, offs);
503                 }
504             }
505             else
506             {
507                 emit->emitIns_R_S(ins_Move_Extend(targetType, treeNode->InReg()), size, targetReg, varNum, offs);
508             }
509         }
510             genProduceReg(treeNode);
511             break;
512
513         case GT_STORE_LCL_FLD:
514         {
515             noway_assert(targetType != TYP_STRUCT);
516
517             // record the offset
518             unsigned offset = treeNode->gtLclFld.gtLclOffs;
519
520             // We must have a stack store with GT_STORE_LCL_FLD
521             noway_assert(!treeNode->InReg());
522             noway_assert(targetReg == REG_NA);
523
524             GenTreeLclVarCommon* varNode = treeNode->AsLclVarCommon();
525             unsigned             varNum  = varNode->gtLclNum;
526             assert(varNum < compiler->lvaCount);
527             LclVarDsc* varDsc = &(compiler->lvaTable[varNum]);
528
529             // Ensure that lclVar nodes are typed correctly.
530             assert(!varDsc->lvNormalizeOnStore() || targetType == genActualType(varDsc->TypeGet()));
531
532             GenTreePtr  data = treeNode->gtOp.gtOp1->gtEffectiveVal();
533             instruction ins  = ins_Store(targetType);
534             emitAttr    attr = emitTypeSize(targetType);
535             if (data->isContainedIntOrIImmed())
536             {
537                 assert(data->IsIntegralConst(0));
538                 NYI_ARM("st.lclFld contained operand");
539             }
540             else
541             {
542                 assert(!data->isContained());
543                 genConsumeReg(data);
544                 emit->emitIns_S_R(ins, attr, data->gtRegNum, varNum, offset);
545             }
546
547             genUpdateLife(varNode);
548             varDsc->lvRegNum = REG_STK;
549         }
550         break;
551
552         case GT_STORE_LCL_VAR:
553         {
554             GenTreeLclVarCommon* varNode = treeNode->AsLclVarCommon();
555
556             unsigned varNum = varNode->gtLclNum;
557             assert(varNum < compiler->lvaCount);
558             LclVarDsc* varDsc = &(compiler->lvaTable[varNum]);
559             unsigned   offset = 0;
560
561             // Ensure that lclVar nodes are typed correctly.
562             assert(!varDsc->lvNormalizeOnStore() || targetType == genActualType(varDsc->TypeGet()));
563
564             GenTreePtr data = treeNode->gtOp.gtOp1->gtEffectiveVal();
565
566             // var = call, where call returns a multi-reg return value
567             // case is handled separately.
568             if (data->gtSkipReloadOrCopy()->IsMultiRegCall())
569             {
570                 genMultiRegCallStoreToLocal(treeNode);
571                 break;
572             }
573             else
574             {
575                 if (treeNode->TypeGet() == TYP_LONG)
576                 {
577                     genStoreLongLclVar(treeNode);
578                     break;
579                 }
580
581                 genConsumeRegs(data);
582
583                 regNumber dataReg = REG_NA;
584                 if (data->isContainedIntOrIImmed())
585                 {
586                     assert(data->IsIntegralConst(0));
587                     NYI_ARM("st.lclVar contained operand");
588                 }
589                 else
590                 {
591                     assert(!data->isContained());
592                     dataReg = data->gtRegNum;
593                 }
594                 assert(dataReg != REG_NA);
595
596                 if (targetReg == REG_NA) // store into stack based LclVar
597                 {
598                     inst_set_SV_var(varNode);
599
600                     instruction ins  = ins_Store(targetType);
601                     emitAttr    attr = emitTypeSize(targetType);
602
603                     emit->emitIns_S_R(ins, attr, dataReg, varNum, offset);
604
605                     genUpdateLife(varNode);
606
607                     varDsc->lvRegNum = REG_STK;
608                 }
609                 else // store into register (i.e move into register)
610                 {
611                     if (dataReg != targetReg)
612                     {
613                         // Assign into targetReg when dataReg (from op1) is not the same register
614                         inst_RV_RV(ins_Copy(targetType), targetReg, dataReg, targetType);
615                     }
616                     genProduceReg(treeNode);
617                 }
618             }
619         }
620         break;
621
622         case GT_RETFILT:
623             // A void GT_RETFILT is the end of a finally. For non-void filter returns we need to load the result in
624             // the return register, if it's not already there. The processing is the same as GT_RETURN.
625             if (targetType != TYP_VOID)
626             {
627                 // For filters, the IL spec says the result is type int32. Further, the only specified legal values
628                 // are 0 or 1, with the use of other values "undefined".
629                 assert(targetType == TYP_INT);
630             }
631
632             __fallthrough;
633
634         case GT_RETURN:
635             genReturn(treeNode);
636             break;
637
638         case GT_LEA:
639         {
640             // if we are here, it is the case where there is an LEA that cannot
641             // be folded into a parent instruction
642             GenTreeAddrMode* lea = treeNode->AsAddrMode();
643             genLeaInstruction(lea);
644         }
645         // genLeaInstruction calls genProduceReg()
646         break;
647
648         case GT_IND:
649             genConsumeAddress(treeNode->AsIndir()->Addr());
650             emit->emitInsLoadStoreOp(ins_Load(targetType), emitTypeSize(treeNode), targetReg, treeNode->AsIndir());
651             genProduceReg(treeNode);
652             break;
653
654         case GT_MOD:
655         case GT_UDIV:
656         case GT_UMOD:
657             // We shouldn't be seeing GT_MOD on float/double args as it should get morphed into a
658             // helper call by front-end.  Similarly we shouldn't be seeing GT_UDIV and GT_UMOD
659             // on float/double args.
660             noway_assert(!varTypeIsFloating(treeNode));
661             __fallthrough;
662
663         case GT_DIV:
664         {
665             genConsumeOperands(treeNode->AsOp());
666
667             noway_assert(targetReg != REG_NA);
668
669             GenTreePtr  dst    = treeNode;
670             GenTreePtr  src1   = treeNode->gtGetOp1();
671             GenTreePtr  src2   = treeNode->gtGetOp2();
672             instruction ins    = genGetInsForOper(treeNode->OperGet(), targetType);
673             emitAttr    attr   = emitTypeSize(treeNode);
674             regNumber   result = REG_NA;
675
676             // dst can only be a reg
677             assert(!dst->isContained());
678
679             // src can be only reg
680             assert(!src1->isContained() || !src2->isContained());
681
682             if (varTypeIsFloating(targetType))
683             {
684                 // Floating point divide never raises an exception
685
686                 emit->emitIns_R_R_R(ins, attr, dst->gtRegNum, src1->gtRegNum, src2->gtRegNum);
687             }
688             else // an signed integer divide operation
689             {
690                 // TODO-ARM-Bug: handle zero division exception.
691
692                 emit->emitIns_R_R_R(ins, attr, dst->gtRegNum, src1->gtRegNum, src2->gtRegNum);
693             }
694
695             genProduceReg(treeNode);
696         }
697         break;
698
699         case GT_INTRINSIC:
700         {
701             genIntrinsic(treeNode);
702         }
703         break;
704
705         case GT_EQ:
706         case GT_NE:
707         case GT_LT:
708         case GT_LE:
709         case GT_GE:
710         case GT_GT:
711         {
712             // TODO-ARM-CQ: Check if we can use the currently set flags.
713             // TODO-ARM-CQ: Check for the case where we can simply transfer the carry bit to a register
714             //         (signed < or >= where targetReg != REG_NA)
715
716             GenTreeOp* tree = treeNode->AsOp();
717             GenTreePtr op1  = tree->gtOp1->gtEffectiveVal();
718             GenTreePtr op2  = tree->gtOp2->gtEffectiveVal();
719
720             genConsumeIfReg(op1);
721             genConsumeIfReg(op2);
722
723             instruction ins = INS_cmp;
724             emitAttr    cmpAttr;
725             if (varTypeIsFloating(op1))
726             {
727                 assert(op1->TypeGet() == op2->TypeGet());
728                 ins     = INS_vcmp;
729                 cmpAttr = emitTypeSize(op1->TypeGet());
730                 emit->emitInsBinary(ins, cmpAttr, op1, op2);
731                 // vmrs with register 0xf has special meaning of transferring flags
732                 emit->emitIns_R(INS_vmrs, EA_4BYTE, REG_R15);
733             }
734             else if (varTypeIsLong(op1))
735             {
736 #ifdef DEBUG
737                 // The result of an unlowered long compare on a 32-bit target must either be
738                 // a) materialized into a register, or
739                 // b) unused.
740                 //
741                 // A long compare that has a result that is used but not materialized into a register should
742                 // have been handled by Lowering::LowerCompare.
743
744                 LIR::Use use;
745                 assert((treeNode->gtRegNum != REG_NA) || !LIR::AsRange(compiler->compCurBB).TryGetUse(treeNode, &use));
746 #endif
747                 genCompareLong(treeNode);
748                 break;
749             }
750             else
751             {
752                 var_types op1Type = op1->TypeGet();
753                 var_types op2Type = op2->TypeGet();
754                 assert(!varTypeIsFloating(op2Type));
755                 ins = INS_cmp;
756                 if (op1Type == op2Type)
757                 {
758                     cmpAttr = emitTypeSize(op1Type);
759                 }
760                 else
761                 {
762                     var_types cmpType    = TYP_INT;
763                     bool      op1Is64Bit = (varTypeIsLong(op1Type) || op1Type == TYP_REF);
764                     bool      op2Is64Bit = (varTypeIsLong(op2Type) || op2Type == TYP_REF);
765                     NYI_IF(op1Is64Bit || op2Is64Bit, "Long compare");
766                     assert(!op1->isUsedFromMemory() || op1Type == op2Type);
767                     assert(!op2->isUsedFromMemory() || op1Type == op2Type);
768                     cmpAttr = emitTypeSize(cmpType);
769                 }
770                 emit->emitInsBinary(ins, cmpAttr, op1, op2);
771             }
772
773             // Are we evaluating this into a register?
774             if (targetReg != REG_NA)
775             {
776                 genSetRegToCond(targetReg, tree);
777                 genProduceReg(tree);
778             }
779         }
780         break;
781
782         case GT_JTRUE:
783             genCodeForJumpTrue(treeNode);
784             break;
785
786         case GT_JCC:
787         {
788             GenTreeJumpCC* jcc = treeNode->AsJumpCC();
789
790             assert(compiler->compCurBB->bbJumpKind == BBJ_COND);
791
792             CompareKind  compareKind = ((jcc->gtFlags & GTF_UNSIGNED) != 0) ? CK_UNSIGNED : CK_SIGNED;
793             emitJumpKind jumpKind    = genJumpKindForOper(jcc->gtCondition, compareKind);
794
795             inst_JMP(jumpKind, compiler->compCurBB->bbJumpDest);
796         }
797         break;
798
799         case GT_RETURNTRAP:
800         {
801             // this is nothing but a conditional call to CORINFO_HELP_STOP_FOR_GC
802             // based on the contents of 'data'
803
804             GenTree* data = treeNode->gtOp.gtOp1->gtEffectiveVal();
805             genConsumeIfReg(data);
806             GenTreeIntCon cns = intForm(TYP_INT, 0);
807             emit->emitInsBinary(INS_cmp, emitTypeSize(TYP_INT), data, &cns);
808
809             BasicBlock* skipLabel = genCreateTempLabel();
810
811             emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED);
812             inst_JMP(jmpEqual, skipLabel);
813             // emit the call to the EE-helper that stops for GC (or other reasons)
814
815             genEmitHelperCall(CORINFO_HELP_STOP_FOR_GC, 0, EA_UNKNOWN);
816             genDefineTempLabel(skipLabel);
817         }
818         break;
819
820         case GT_STOREIND:
821         {
822             GenTreeStoreInd* storeInd   = treeNode->AsStoreInd();
823             GenTree*         data       = storeInd->Data();
824             GenTree*         addr       = storeInd->Addr();
825             var_types        targetType = storeInd->TypeGet();
826
827             assert(!varTypeIsFloating(targetType) || (targetType == data->TypeGet()));
828
829             GCInfo::WriteBarrierForm writeBarrierForm = gcInfo.gcIsWriteBarrierCandidate(treeNode, data);
830             if (writeBarrierForm != GCInfo::WBF_NoBarrier)
831             {
832                 // data and addr must be in registers.
833                 // Consume both registers so that any copies of interfering
834                 // registers are taken care of.
835                 genConsumeOperands(storeInd->AsOp());
836
837 #if NOGC_WRITE_BARRIERS
838                 NYI_ARM("NOGC_WRITE_BARRIERS");
839 #else
840                 // At this point, we should not have any interference.
841                 // That is, 'data' must not be in REG_ARG_0,
842                 //  as that is where 'addr' must go.
843                 noway_assert(data->gtRegNum != REG_ARG_0);
844
845                 // addr goes in REG_ARG_0
846                 if (addr->gtRegNum != REG_ARG_0)
847                 {
848                     inst_RV_RV(INS_mov, REG_ARG_0, addr->gtRegNum, addr->TypeGet());
849                 }
850
851                 // data goes in REG_ARG_1
852                 if (data->gtRegNum != REG_ARG_1)
853                 {
854                     inst_RV_RV(INS_mov, REG_ARG_1, data->gtRegNum, data->TypeGet());
855                 }
856 #endif // NOGC_WRITE_BARRIERS
857
858                 genGCWriteBarrier(storeInd, writeBarrierForm);
859             }
860             else // A normal store, not a WriteBarrier store
861             {
862                 bool reverseOps  = ((storeInd->gtFlags & GTF_REVERSE_OPS) != 0);
863                 bool dataIsUnary = false;
864
865                 // We must consume the operands in the proper execution order,
866                 // so that liveness is updated appropriately.
867                 if (!reverseOps)
868                 {
869                     genConsumeAddress(addr);
870                 }
871
872                 if (!data->isContained())
873                 {
874                     genConsumeRegs(data);
875                 }
876
877                 if (reverseOps)
878                 {
879                     genConsumeAddress(addr);
880                 }
881
882                 emit->emitInsLoadStoreOp(ins_Store(targetType), emitTypeSize(storeInd), data->gtRegNum,
883                                          treeNode->AsIndir());
884             }
885         }
886         break;
887
888         case GT_COPY:
889             // This is handled at the time we call genConsumeReg() on the GT_COPY
890             break;
891
892         case GT_LIST:
893         case GT_FIELD_LIST:
894         case GT_ARGPLACE:
895             // Nothing to do
896             break;
897
898         case GT_PUTARG_STK:
899             genPutArgStk(treeNode->AsPutArgStk());
900             break;
901
902         case GT_PUTARG_REG:
903             assert(targetType != TYP_STRUCT); // Any TYP_STRUCT register args should have been removed by
904                                               // fgMorphMultiregStructArg
905             // We have a normal non-Struct targetType
906             {
907                 GenTree* op1 = treeNode->gtOp.gtOp1;
908                 // If child node is not already in the register we need, move it
909                 genConsumeReg(op1);
910                 if (targetReg != op1->gtRegNum)
911                 {
912                     inst_RV_RV(ins_Copy(targetType), targetReg, op1->gtRegNum, targetType);
913                 }
914             }
915             genProduceReg(treeNode);
916             break;
917
918         case GT_CALL:
919             genCallInstruction(treeNode->AsCall());
920             break;
921
922         case GT_LOCKADD:
923         case GT_XCHG:
924         case GT_XADD:
925             genLockedInstructions(treeNode->AsOp());
926             break;
927
928         case GT_MEMORYBARRIER:
929             instGen_MemoryBarrier();
930             break;
931
932         case GT_CMPXCHG:
933         {
934             NYI("GT_CMPXCHG");
935         }
936             genProduceReg(treeNode);
937             break;
938
939         case GT_RELOAD:
940             // do nothing - reload is just a marker.
941             // The parent node will call genConsumeReg on this which will trigger the unspill of this node's child
942             // into the register specified in this node.
943             break;
944
945         case GT_NOP:
946             break;
947
948         case GT_NO_OP:
949             if (treeNode->gtFlags & GTF_NO_OP_NO)
950             {
951                 noway_assert(!"GTF_NO_OP_NO should not be set");
952             }
953             else
954             {
955                 instGen(INS_nop);
956             }
957             break;
958
959         case GT_ARR_BOUNDS_CHECK:
960             genRangeCheck(treeNode);
961             break;
962
963         case GT_PHYSREG:
964             if (treeNode->gtRegNum != treeNode->AsPhysReg()->gtSrcReg)
965             {
966                 inst_RV_RV(INS_mov, treeNode->gtRegNum, treeNode->AsPhysReg()->gtSrcReg, targetType);
967
968                 genTransferRegGCState(treeNode->gtRegNum, treeNode->AsPhysReg()->gtSrcReg);
969             }
970             break;
971
972         case GT_PHYSREGDST:
973             break;
974
975         case GT_NULLCHECK:
976         {
977             assert(!treeNode->gtOp.gtOp1->isContained());
978             regNumber addrReg = genConsumeReg(treeNode->gtOp.gtOp1);
979             emit->emitIns_R_R_I(INS_ldr, EA_4BYTE, targetReg, addrReg, 0);
980         }
981         break;
982
983         case GT_CATCH_ARG:
984
985             noway_assert(handlerGetsXcptnObj(compiler->compCurBB->bbCatchTyp));
986
987             /* Catch arguments get passed in a register. genCodeForBBlist()
988                would have marked it as holding a GC object, but not used. */
989
990             noway_assert(gcInfo.gcRegGCrefSetCur & RBM_EXCEPTION_OBJECT);
991             genConsumeReg(treeNode);
992             break;
993
994         case GT_PINVOKE_PROLOG:
995             noway_assert(((gcInfo.gcRegGCrefSetCur | gcInfo.gcRegByrefSetCur) & ~fullIntArgRegMask()) == 0);
996
997             // the runtime side requires the codegen here to be consistent
998             emit->emitDisableRandomNops();
999             break;
1000
1001         case GT_LABEL:
1002             genPendingCallLabel       = genCreateTempLabel();
1003             treeNode->gtLabel.gtLabBB = genPendingCallLabel;
1004             emit->emitIns_J_R(INS_adr, EA_PTRSIZE, genPendingCallLabel, treeNode->gtRegNum);
1005             break;
1006
1007         case GT_CLS_VAR_ADDR:
1008             emit->emitIns_R_C(INS_lea, EA_PTRSIZE, targetReg, treeNode->gtClsVar.gtClsVarHnd, 0);
1009             genProduceReg(treeNode);
1010             break;
1011
1012         case GT_STORE_DYN_BLK:
1013         case GT_STORE_BLK:
1014             genCodeForStoreBlk(treeNode->AsBlk());
1015             break;
1016
1017         case GT_JMPTABLE:
1018             genJumpTable(treeNode);
1019             break;
1020
1021         case GT_SWITCH_TABLE:
1022             genTableBasedSwitch(treeNode);
1023             break;
1024
1025         case GT_ARR_INDEX:
1026             genCodeForArrIndex(treeNode->AsArrIndex());
1027             break;
1028
1029         case GT_ARR_OFFSET:
1030             genCodeForArrOffset(treeNode->AsArrOffs());
1031             break;
1032
1033         case GT_IL_OFFSET:
1034             // Do nothing; these nodes are simply markers for debug info.
1035             break;
1036
1037         default:
1038         {
1039 #ifdef DEBUG
1040             char message[256];
1041             _snprintf_s(message, _countof(message), _TRUNCATE, "NYI: Unimplemented node type %s",
1042                         GenTree::NodeName(treeNode->OperGet()));
1043             NYIRAW(message);
1044 #else
1045             NYI("unimplemented node");
1046 #endif
1047         }
1048         break;
1049     }
1050 }
1051
1052 //------------------------------------------------------------------------
1053 // genLockedInstructions: Generate code for the locked operations.
1054 //
1055 // Notes:
1056 //    Handles GT_LOCKADD, GT_XCHG, GT_XADD nodes.
1057 //
1058 void CodeGen::genLockedInstructions(GenTreeOp* treeNode)
1059 {
1060     NYI("genLockedInstructions");
1061 }
1062
1063 //--------------------------------------------------------------------------------------
1064 // genLclHeap: Generate code for localloc
1065 //
1066 // Description:
1067 //      There are 2 ways depending from build version to generate code for localloc:
1068 //          1) For debug build where memory should be initialized we generate loop
1069 //             which invoke push {tmpReg} N times.
1070 //          2) Fore /o build  However, we tickle the pages to ensure that SP is always
1071 //             valid and is in sync with the "stack guard page". Amount of iteration
1072 //             is N/PAGE_SIZE.
1073 //
1074 // Comments:
1075 //      There can be some optimization:
1076 //          1) It's not needed to generate loop for zero size allocation
1077 //          2) For small allocation (less than 4 store) we unroll loop
1078 //          3) For allocation less than PAGE_SIZE and when it's not needed to initialize
1079 //             memory to zero, we can just increment SP.
1080 //
1081 // Notes: Size N should be aligned to STACK_ALIGN before any allocation
1082 //
1083 void CodeGen::genLclHeap(GenTreePtr tree)
1084 {
1085     assert(tree->OperGet() == GT_LCLHEAP);
1086
1087     GenTreePtr size = tree->gtOp.gtOp1;
1088     noway_assert((genActualType(size->gtType) == TYP_INT) || (genActualType(size->gtType) == TYP_I_IMPL));
1089
1090     // Result of localloc will be returned in regCnt.
1091     // Also it used as temporary register in code generation
1092     // for storing allocation size
1093     regNumber   regCnt          = tree->gtRegNum;
1094     regNumber   pspSymReg       = REG_NA;
1095     var_types   type            = genActualType(size->gtType);
1096     emitAttr    easz            = emitTypeSize(type);
1097     BasicBlock* endLabel        = nullptr;
1098     BasicBlock* loop            = nullptr;
1099     unsigned    stackAdjustment = 0;
1100
1101 #ifdef DEBUG
1102     // Verify ESP
1103     if (compiler->opts.compStackCheckOnRet)
1104     {
1105         noway_assert(compiler->lvaReturnEspCheck != 0xCCCCCCCC &&
1106                      compiler->lvaTable[compiler->lvaReturnEspCheck].lvDoNotEnregister &&
1107                      compiler->lvaTable[compiler->lvaReturnEspCheck].lvOnFrame);
1108         getEmitter()->emitIns_S_R(INS_cmp, EA_PTRSIZE, REG_SPBASE, compiler->lvaReturnEspCheck, 0);
1109
1110         BasicBlock*  esp_check = genCreateTempLabel();
1111         emitJumpKind jmpEqual  = genJumpKindForOper(GT_EQ, CK_SIGNED);
1112         inst_JMP(jmpEqual, esp_check);
1113         getEmitter()->emitIns(INS_BREAKPOINT);
1114         genDefineTempLabel(esp_check);
1115     }
1116 #endif
1117
1118     noway_assert(isFramePointerUsed()); // localloc requires Frame Pointer to be established since SP changes
1119     noway_assert(genStackLevel == 0);   // Can't have anything on the stack
1120
1121     // Whether method has PSPSym.
1122     bool hasPspSym;
1123 #if FEATURE_EH_FUNCLETS
1124     hasPspSym = (compiler->lvaPSPSym != BAD_VAR_NUM);
1125 #else
1126     hasPspSym = false;
1127 #endif
1128
1129     // Check to 0 size allocations
1130     // size_t amount = 0;
1131     if (size->IsCnsIntOrI())
1132     {
1133         // If size is a constant, then it must be contained.
1134         assert(size->isContained());
1135
1136         // If amount is zero then return null in regCnt
1137         size_t amount = size->gtIntCon.gtIconVal;
1138         if (amount == 0)
1139         {
1140             instGen_Set_Reg_To_Zero(EA_PTRSIZE, regCnt);
1141             goto BAILOUT;
1142         }
1143     }
1144     else
1145     {
1146         // If 0 bail out by returning null in regCnt
1147         genConsumeRegAndCopy(size, regCnt);
1148         endLabel = genCreateTempLabel();
1149         getEmitter()->emitIns_R_R(INS_TEST, easz, regCnt, regCnt);
1150         emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED);
1151         inst_JMP(jmpEqual, endLabel);
1152     }
1153
1154     stackAdjustment = 0;
1155 #if FEATURE_EH_FUNCLETS
1156     // If we have PSPsym, then need to re-locate it after localloc.
1157     if (hasPspSym)
1158     {
1159         stackAdjustment += STACK_ALIGN;
1160
1161         // Save a copy of PSPSym
1162         pspSymReg = tree->ExtractTempReg();
1163         getEmitter()->emitIns_R_S(ins_Load(TYP_I_IMPL), EA_PTRSIZE, pspSymReg, compiler->lvaPSPSym, 0);
1164     }
1165 #endif
1166
1167 #if FEATURE_FIXED_OUT_ARGS
1168     // If we have an outgoing arg area then we must adjust the SP by popping off the
1169     // outgoing arg area. We will restore it right before we return from this method.
1170     if (compiler->lvaOutgoingArgSpaceSize > 0)
1171     {
1172         assert((compiler->lvaOutgoingArgSpaceSize % STACK_ALIGN) == 0); // This must be true for the stack to remain
1173                                                                         // aligned
1174         inst_RV_IV(INS_add, REG_SPBASE, compiler->lvaOutgoingArgSpaceSize, EA_PTRSIZE);
1175         stackAdjustment += compiler->lvaOutgoingArgSpaceSize;
1176     }
1177 #endif
1178
1179     // Put aligned allocation size to regCnt
1180     if (size->IsCnsIntOrI())
1181     {
1182         // 'amount' is the total number of bytes to localloc to properly STACK_ALIGN
1183         size_t amount = size->gtIntCon.gtIconVal;
1184         amount        = AlignUp(amount, STACK_ALIGN);
1185
1186         // For small allocations we will generate up to four stp instructions
1187         size_t cntStackAlignedWidthItems = (amount >> STACK_ALIGN_SHIFT);
1188         if (cntStackAlignedWidthItems <= 4)
1189         {
1190             instGen_Set_Reg_To_Zero(EA_PTRSIZE, regCnt);
1191
1192             while (cntStackAlignedWidthItems != 0)
1193             {
1194                 inst_IV(INS_push, (unsigned)genRegMask(regCnt));
1195                 cntStackAlignedWidthItems -= 1;
1196             }
1197
1198             goto ALLOC_DONE;
1199         }
1200         else if (!compiler->info.compInitMem && (amount < compiler->eeGetPageSize())) // must be < not <=
1201         {
1202             // Since the size is a page or less, simply adjust the SP value
1203             // The SP might already be in the guard page, must touch it BEFORE
1204             // the alloc, not after.
1205             getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, regCnt, REG_SP, 0);
1206             inst_RV_IV(INS_sub, REG_SP, amount, EA_PTRSIZE);
1207             goto ALLOC_DONE;
1208         }
1209
1210         // regCnt will be the total number of bytes to locAlloc
1211         genSetRegToIcon(regCnt, amount, ((int)amount == amount) ? TYP_INT : TYP_LONG);
1212     }
1213     else
1214     {
1215         // Round up the number of bytes to allocate to a STACK_ALIGN boundary.
1216         inst_RV_IV(INS_add, regCnt, (STACK_ALIGN - 1), emitActualTypeSize(type));
1217         inst_RV_IV(INS_AND, regCnt, ~(STACK_ALIGN - 1), emitActualTypeSize(type));
1218     }
1219
1220     // Allocation
1221     if (compiler->info.compInitMem)
1222     {
1223         // At this point 'regCnt' is set to the total number of bytes to locAlloc.
1224         // Since we have to zero out the allocated memory AND ensure that RSP is always valid
1225         // by tickling the pages, we will just push 0's on the stack.
1226
1227         regNumber regTmp = tree->ExtractTempReg();
1228         instGen_Set_Reg_To_Zero(EA_PTRSIZE, regTmp);
1229
1230         // Loop:
1231         BasicBlock* loop = genCreateTempLabel();
1232         genDefineTempLabel(loop);
1233
1234         noway_assert(STACK_ALIGN == 8);
1235         inst_IV(INS_push, (unsigned)genRegMask(regTmp));
1236         inst_IV(INS_push, (unsigned)genRegMask(regTmp));
1237
1238         // If not done, loop
1239         // Note that regCnt is the number of bytes to stack allocate.
1240         assert(genIsValidIntReg(regCnt));
1241         getEmitter()->emitIns_R_I(INS_sub, EA_PTRSIZE, regCnt, STACK_ALIGN, INS_FLAGS_SET);
1242         emitJumpKind jmpNotEqual = genJumpKindForOper(GT_NE, CK_SIGNED);
1243         inst_JMP(jmpNotEqual, loop);
1244     }
1245     else
1246     {
1247         // At this point 'regCnt' is set to the total number of bytes to locAlloc.
1248         //
1249         // We don't need to zero out the allocated memory. However, we do have
1250         // to tickle the pages to ensure that SP is always valid and is
1251         // in sync with the "stack guard page".  Note that in the worst
1252         // case SP is on the last byte of the guard page.  Thus you must
1253         // touch SP+0 first not SP+0x1000.
1254         //
1255         // Another subtlety is that you don't want SP to be exactly on the
1256         // boundary of the guard page because PUSH is predecrement, thus
1257         // call setup would not touch the guard page but just beyond it
1258         //
1259         // Note that we go through a few hoops so that SP never points to
1260         // illegal pages at any time during the ticking process
1261         //
1262         //       subs  regCnt, SP, regCnt      // regCnt now holds ultimate SP
1263         //       jb    Loop                    // result is smaller than orignial SP (no wrap around)
1264         //       mov   regCnt, #0              // Overflow, pick lowest possible value
1265         //
1266         //  Loop:
1267         //       ldr   regTmp, [SP + 0]        // tickle the page - read from the page
1268         //       sub   regTmp, SP, PAGE_SIZE   // decrement SP by PAGE_SIZE
1269         //       cmp   regTmp, regCnt
1270         //       jb    Done
1271         //       mov   SP, regTmp
1272         //       j     Loop
1273         //
1274         //  Done:
1275         //       mov   SP, regCnt
1276         //
1277
1278         // Setup the regTmp
1279         regNumber regTmp = tree->ExtractTempReg();
1280
1281         BasicBlock* loop = genCreateTempLabel();
1282         BasicBlock* done = genCreateTempLabel();
1283
1284         //       subs  regCnt, SP, regCnt      // regCnt now holds ultimate SP
1285         getEmitter()->emitIns_R_R_R(INS_sub, EA_PTRSIZE, regCnt, REG_SPBASE, regCnt, INS_FLAGS_SET);
1286
1287         inst_JMP(EJ_vc, loop); // branch if the V flag is not set
1288
1289         // Ups... Overflow, set regCnt to lowest possible value
1290         instGen_Set_Reg_To_Zero(EA_PTRSIZE, regCnt);
1291
1292         genDefineTempLabel(loop);
1293
1294         // tickle the page - Read from the updated SP - this triggers a page fault when on the guard page
1295         getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, regTmp, REG_SPBASE, 0);
1296
1297         // decrement SP by PAGE_SIZE
1298         getEmitter()->emitIns_R_R_I(INS_sub, EA_PTRSIZE, regTmp, REG_SPBASE, compiler->eeGetPageSize());
1299
1300         getEmitter()->emitIns_R_R(INS_cmp, EA_PTRSIZE, regTmp, regCnt);
1301         emitJumpKind jmpLTU = genJumpKindForOper(GT_LT, CK_UNSIGNED);
1302         inst_JMP(jmpLTU, done);
1303
1304         // Update SP to be at the next page of stack that we will tickle
1305         getEmitter()->emitIns_R_R(INS_mov, EA_PTRSIZE, REG_SPBASE, regCnt);
1306
1307         // Jump to loop and tickle new stack address
1308         inst_JMP(EJ_jmp, loop);
1309
1310         // Done with stack tickle loop
1311         genDefineTempLabel(done);
1312
1313         // Now just move the final value to SP
1314         getEmitter()->emitIns_R_R(INS_mov, EA_PTRSIZE, REG_SPBASE, regCnt);
1315     }
1316
1317 ALLOC_DONE:
1318     // Re-adjust SP to allocate PSPSym and out-going arg area
1319     if (stackAdjustment != 0)
1320     {
1321         assert((stackAdjustment % STACK_ALIGN) == 0); // This must be true for the stack to remain aligned
1322         assert(stackAdjustment > 0);
1323         getEmitter()->emitIns_R_R_I(INS_sub, EA_PTRSIZE, REG_SPBASE, REG_SPBASE, (int)stackAdjustment);
1324
1325 #if FEATURE_EH_FUNCLETS
1326         // Write PSPSym to its new location.
1327         if (hasPspSym)
1328         {
1329             assert(genIsValidIntReg(pspSymReg));
1330             getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, pspSymReg, compiler->lvaPSPSym, 0);
1331         }
1332 #endif
1333         // Return the stackalloc'ed address in result register.
1334         // regCnt = RSP + stackAdjustment.
1335         getEmitter()->emitIns_R_R_I(INS_add, EA_PTRSIZE, regCnt, REG_SPBASE, (int)stackAdjustment);
1336     }
1337     else // stackAdjustment == 0
1338     {
1339         // Move the final value of SP to regCnt
1340         inst_RV_RV(INS_mov, regCnt, REG_SPBASE);
1341     }
1342
1343 BAILOUT:
1344     if (endLabel != nullptr)
1345         genDefineTempLabel(endLabel);
1346
1347     // Write the lvaLocAllocSPvar stack frame slot
1348     if (compiler->lvaLocAllocSPvar != BAD_VAR_NUM)
1349     {
1350         getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, regCnt, compiler->lvaLocAllocSPvar, 0);
1351     }
1352
1353 #if STACK_PROBES
1354     if (compiler->opts.compNeedStackProbes)
1355     {
1356         genGenerateStackProbe();
1357     }
1358 #endif
1359
1360 #ifdef DEBUG
1361     // Update new ESP
1362     if (compiler->opts.compStackCheckOnRet)
1363     {
1364         noway_assert(compiler->lvaReturnEspCheck != 0xCCCCCCCC &&
1365                      compiler->lvaTable[compiler->lvaReturnEspCheck].lvDoNotEnregister &&
1366                      compiler->lvaTable[compiler->lvaReturnEspCheck].lvOnFrame);
1367         getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, regCnt, compiler->lvaReturnEspCheck, 0);
1368     }
1369 #endif
1370
1371     genProduceReg(tree);
1372 }
1373
1374 //------------------------------------------------------------------------
1375 // genTableBasedSwitch: generate code for a switch statement based on a table of ip-relative offsets
1376 //
1377 void CodeGen::genTableBasedSwitch(GenTree* treeNode)
1378 {
1379     genConsumeOperands(treeNode->AsOp());
1380     regNumber idxReg  = treeNode->gtOp.gtOp1->gtRegNum;
1381     regNumber baseReg = treeNode->gtOp.gtOp2->gtRegNum;
1382
1383     getEmitter()->emitIns_R_ARX(INS_ldr, EA_4BYTE, REG_PC, baseReg, idxReg, TARGET_POINTER_SIZE, 0);
1384 }
1385
1386 //------------------------------------------------------------------------
1387 // genJumpTable: emits the table and an instruction to get the address of the first element
1388 //
1389 void CodeGen::genJumpTable(GenTree* treeNode)
1390 {
1391     noway_assert(compiler->compCurBB->bbJumpKind == BBJ_SWITCH);
1392     assert(treeNode->OperGet() == GT_JMPTABLE);
1393
1394     unsigned     jumpCount = compiler->compCurBB->bbJumpSwt->bbsCount;
1395     BasicBlock** jumpTable = compiler->compCurBB->bbJumpSwt->bbsDstTab;
1396     unsigned     jmpTabBase;
1397
1398     jmpTabBase = getEmitter()->emitBBTableDataGenBeg(jumpCount, false);
1399
1400     JITDUMP("\n      J_M%03u_DS%02u LABEL   DWORD\n", Compiler::s_compMethodsCount, jmpTabBase);
1401
1402     for (unsigned i = 0; i < jumpCount; i++)
1403     {
1404         BasicBlock* target = *jumpTable++;
1405         noway_assert(target->bbFlags & BBF_JMP_TARGET);
1406
1407         JITDUMP("            DD      L_M%03u_BB%02u\n", Compiler::s_compMethodsCount, target->bbNum);
1408
1409         getEmitter()->emitDataGenData(i, target);
1410     }
1411
1412     getEmitter()->emitDataGenEnd();
1413
1414     getEmitter()->emitIns_R_D(INS_movw, EA_HANDLE_CNS_RELOC, jmpTabBase, treeNode->gtRegNum);
1415     getEmitter()->emitIns_R_D(INS_movt, EA_HANDLE_CNS_RELOC, jmpTabBase, treeNode->gtRegNum);
1416
1417     genProduceReg(treeNode);
1418 }
1419
1420 //------------------------------------------------------------------------
1421 // genGetInsForOper: Return instruction encoding of the operation tree.
1422 //
1423 instruction CodeGen::genGetInsForOper(genTreeOps oper, var_types type)
1424 {
1425     instruction ins;
1426
1427     if (varTypeIsFloating(type))
1428         return CodeGen::ins_MathOp(oper, type);
1429
1430     switch (oper)
1431     {
1432         case GT_ADD:
1433             ins = INS_add;
1434             break;
1435         case GT_AND:
1436             ins = INS_AND;
1437             break;
1438         case GT_MUL:
1439             ins = INS_MUL;
1440             break;
1441         case GT_DIV:
1442             ins = INS_sdiv;
1443             break;
1444         case GT_LSH:
1445             ins = INS_SHIFT_LEFT_LOGICAL;
1446             break;
1447         case GT_NEG:
1448             ins = INS_rsb;
1449             break;
1450         case GT_NOT:
1451             ins = INS_NOT;
1452             break;
1453         case GT_OR:
1454             ins = INS_OR;
1455             break;
1456         case GT_RSH:
1457             ins = INS_SHIFT_RIGHT_ARITHM;
1458             break;
1459         case GT_RSZ:
1460             ins = INS_SHIFT_RIGHT_LOGICAL;
1461             break;
1462         case GT_SUB:
1463             ins = INS_sub;
1464             break;
1465         case GT_XOR:
1466             ins = INS_XOR;
1467             break;
1468         case GT_ROR:
1469             ins = INS_ror;
1470             break;
1471         case GT_ADD_LO:
1472             ins = INS_add;
1473             break;
1474         case GT_ADD_HI:
1475             ins = INS_adc;
1476             break;
1477         case GT_SUB_LO:
1478             ins = INS_sub;
1479             break;
1480         case GT_SUB_HI:
1481             ins = INS_sbc;
1482             break;
1483         case GT_LSH_HI:
1484             ins = INS_SHIFT_LEFT_LOGICAL;
1485             break;
1486         case GT_RSH_LO:
1487             ins = INS_SHIFT_RIGHT_LOGICAL;
1488             break;
1489         default:
1490             unreached();
1491             break;
1492     }
1493     return ins;
1494 }
1495
1496 // Generates CpBlk code by performing a loop unroll
1497 // Preconditions:
1498 //  The size argument of the CpBlk node is a constant and <= 64 bytes.
1499 //  This may seem small but covers >95% of the cases in several framework assemblies.
1500 void CodeGen::genCodeForCpBlkUnroll(GenTreeBlk* cpBlkNode)
1501 {
1502     NYI_ARM("genCodeForCpBlkUnroll");
1503 }
1504
1505 // Generate code for InitBlk by performing a loop unroll
1506 // Preconditions:
1507 //   a) Both the size and fill byte value are integer constants.
1508 //   b) The size of the struct to initialize is smaller than INITBLK_UNROLL_LIMIT bytes.
1509 void CodeGen::genCodeForInitBlkUnroll(GenTreeBlk* initBlkNode)
1510 {
1511     NYI_ARM("genCodeForInitBlkUnroll");
1512 }
1513
1514 void CodeGen::genCodeForStoreBlk(GenTreeBlk* blkOp)
1515 {
1516     if (blkOp->gtBlkOpGcUnsafe)
1517     {
1518         getEmitter()->emitDisableGC();
1519     }
1520     bool isCopyBlk = blkOp->OperIsCopyBlkOp();
1521
1522     switch (blkOp->gtBlkOpKind)
1523     {
1524         case GenTreeBlk::BlkOpKindHelper:
1525             if (isCopyBlk)
1526             {
1527                 genCodeForCpBlk(blkOp);
1528             }
1529             else
1530             {
1531                 genCodeForInitBlk(blkOp);
1532             }
1533             break;
1534         case GenTreeBlk::BlkOpKindUnroll:
1535             if (isCopyBlk)
1536             {
1537                 genCodeForCpBlkUnroll(blkOp);
1538             }
1539             else
1540             {
1541                 genCodeForInitBlkUnroll(blkOp);
1542             }
1543             break;
1544         default:
1545             unreached();
1546     }
1547     if (blkOp->gtBlkOpGcUnsafe)
1548     {
1549         getEmitter()->emitEnableGC();
1550     }
1551 }
1552
1553 //------------------------------------------------------------------------
1554 // genCodeForShiftLong: Generates the code sequence for a GenTree node that
1555 // represents a three operand bit shift or rotate operation (<<Hi, >>Lo).
1556 //
1557 // Arguments:
1558 //    tree - the bit shift node (that specifies the type of bit shift to perform).
1559 //
1560 // Assumptions:
1561 //    a) All GenTrees are register allocated.
1562 //    b) The shift-by-amount in tree->gtOp.gtOp2 is a contained constant
1563 //
1564 void CodeGen::genCodeForShiftLong(GenTreePtr tree)
1565 {
1566     // Only the non-RMW case here.
1567     genTreeOps oper = tree->OperGet();
1568     assert(oper == GT_LSH_HI || oper == GT_RSH_LO);
1569
1570     GenTree* operand = tree->gtOp.gtOp1;
1571     assert(operand->OperGet() == GT_LONG);
1572     assert(operand->gtOp.gtOp1->isUsedFromReg());
1573     assert(operand->gtOp.gtOp2->isUsedFromReg());
1574
1575     GenTree* operandLo = operand->gtGetOp1();
1576     GenTree* operandHi = operand->gtGetOp2();
1577
1578     regNumber regLo = operandLo->gtRegNum;
1579     regNumber regHi = operandHi->gtRegNum;
1580
1581     genConsumeOperands(tree->AsOp());
1582
1583     var_types   targetType = tree->TypeGet();
1584     instruction ins        = genGetInsForOper(oper, targetType);
1585
1586     GenTreePtr shiftBy = tree->gtGetOp2();
1587
1588     assert(shiftBy->isContainedIntOrIImmed());
1589
1590     unsigned int count = shiftBy->AsIntConCommon()->IconValue();
1591
1592     regNumber regResult = (oper == GT_LSH_HI) ? regHi : regLo;
1593
1594     if (regResult != tree->gtRegNum)
1595     {
1596         inst_RV_RV(INS_mov, tree->gtRegNum, regResult, targetType);
1597     }
1598
1599     if (oper == GT_LSH_HI)
1600     {
1601         inst_RV_SH(ins, EA_4BYTE, tree->gtRegNum, count);
1602         getEmitter()->emitIns_R_R_R_I(INS_OR, EA_4BYTE, tree->gtRegNum, tree->gtRegNum, regLo, 32 - count,
1603                                       INS_FLAGS_DONT_CARE, INS_OPTS_LSR);
1604     }
1605     else
1606     {
1607         assert(oper == GT_RSH_LO);
1608         inst_RV_SH(INS_SHIFT_RIGHT_LOGICAL, EA_4BYTE, tree->gtRegNum, count);
1609         getEmitter()->emitIns_R_R_R_I(INS_OR, EA_4BYTE, tree->gtRegNum, tree->gtRegNum, regHi, 32 - count,
1610                                       INS_FLAGS_DONT_CARE, INS_OPTS_LSL);
1611     }
1612
1613     genProduceReg(tree);
1614 }
1615
1616 //------------------------------------------------------------------------
1617 // genLeaInstruction: Produce code for a GT_LEA subnode.
1618 //
1619 void CodeGen::genLeaInstruction(GenTreeAddrMode* lea)
1620 {
1621     emitAttr size = emitTypeSize(lea);
1622     genConsumeOperands(lea);
1623
1624     if (lea->Base() && lea->Index())
1625     {
1626         regNumber baseReg  = lea->Base()->gtRegNum;
1627         regNumber indexReg = lea->Index()->gtRegNum;
1628         getEmitter()->emitIns_R_ARX(INS_lea, size, lea->gtRegNum, baseReg, indexReg, lea->gtScale, lea->gtOffset);
1629     }
1630     else if (lea->Base())
1631     {
1632         regNumber baseReg = lea->Base()->gtRegNum;
1633         getEmitter()->emitIns_R_AR(INS_lea, size, lea->gtRegNum, baseReg, lea->gtOffset);
1634     }
1635     else if (lea->Index())
1636     {
1637         assert(!"Should we see a baseless address computation during CodeGen for ARM32?");
1638     }
1639
1640     genProduceReg(lea);
1641 }
1642
1643 //------------------------------------------------------------------------
1644 // genCompareLong: Generate code for comparing two longs when the result of the compare
1645 // is manifested in a register.
1646 //
1647 // Arguments:
1648 //    treeNode - the compare tree
1649 //
1650 // Return Value:
1651 //    None.
1652 //
1653 // Comments:
1654 // For long compares, we need to compare the high parts of operands first, then the low parts.
1655 // If the high compare is false, we do not need to compare the low parts. For less than and
1656 // greater than, if the high compare is true, we can assume the entire compare is true.
1657 //
1658 void CodeGen::genCompareLong(GenTreePtr treeNode)
1659 {
1660     assert(treeNode->OperIsCompare());
1661
1662     GenTreeOp* tree = treeNode->AsOp();
1663     GenTreePtr op1  = tree->gtOp1;
1664     GenTreePtr op2  = tree->gtOp2;
1665
1666     assert(varTypeIsLong(op1->TypeGet()));
1667     assert(varTypeIsLong(op2->TypeGet()));
1668
1669     regNumber targetReg = treeNode->gtRegNum;
1670
1671     genConsumeOperands(tree);
1672
1673     GenTreePtr loOp1 = op1->gtGetOp1();
1674     GenTreePtr hiOp1 = op1->gtGetOp2();
1675     GenTreePtr loOp2 = op2->gtGetOp1();
1676     GenTreePtr hiOp2 = op2->gtGetOp2();
1677
1678     // Create compare for the high parts
1679     instruction ins     = INS_cmp;
1680     var_types   cmpType = TYP_INT;
1681     emitAttr    cmpAttr = emitTypeSize(cmpType);
1682
1683     // Emit the compare instruction
1684     getEmitter()->emitInsBinary(ins, cmpAttr, hiOp1, hiOp2);
1685
1686     // If the result is not being materialized in a register, we're done.
1687     if (targetReg == REG_NA)
1688     {
1689         return;
1690     }
1691
1692     BasicBlock* labelTrue  = genCreateTempLabel();
1693     BasicBlock* labelFalse = genCreateTempLabel();
1694     BasicBlock* labelNext  = genCreateTempLabel();
1695
1696     genJccLongHi(tree->gtOper, labelTrue, labelFalse, tree->IsUnsigned());
1697     getEmitter()->emitInsBinary(ins, cmpAttr, loOp1, loOp2);
1698     genJccLongLo(tree->gtOper, labelTrue, labelFalse);
1699
1700     genDefineTempLabel(labelFalse);
1701     getEmitter()->emitIns_R_I(INS_mov, emitActualTypeSize(tree->gtType), tree->gtRegNum, 0);
1702     getEmitter()->emitIns_J(INS_b, labelNext);
1703
1704     genDefineTempLabel(labelTrue);
1705     getEmitter()->emitIns_R_I(INS_mov, emitActualTypeSize(tree->gtType), tree->gtRegNum, 1);
1706
1707     genDefineTempLabel(labelNext);
1708
1709     genProduceReg(tree);
1710 }
1711
1712 void CodeGen::genJccLongHi(genTreeOps cmp, BasicBlock* jumpTrue, BasicBlock* jumpFalse, bool isUnsigned)
1713 {
1714     if (cmp != GT_NE)
1715     {
1716         jumpFalse->bbFlags |= BBF_JMP_TARGET | BBF_HAS_LABEL;
1717     }
1718
1719     switch (cmp)
1720     {
1721         case GT_EQ:
1722             inst_JMP(EJ_ne, jumpFalse);
1723             break;
1724
1725         case GT_NE:
1726             inst_JMP(EJ_ne, jumpTrue);
1727             break;
1728
1729         case GT_LT:
1730         case GT_LE:
1731             if (isUnsigned)
1732             {
1733                 inst_JMP(EJ_hi, jumpFalse);
1734                 inst_JMP(EJ_lo, jumpTrue);
1735             }
1736             else
1737             {
1738                 inst_JMP(EJ_gt, jumpFalse);
1739                 inst_JMP(EJ_lt, jumpTrue);
1740             }
1741             break;
1742
1743         case GT_GE:
1744         case GT_GT:
1745             if (isUnsigned)
1746             {
1747                 inst_JMP(EJ_lo, jumpFalse);
1748                 inst_JMP(EJ_hi, jumpTrue);
1749             }
1750             else
1751             {
1752                 inst_JMP(EJ_lt, jumpFalse);
1753                 inst_JMP(EJ_gt, jumpTrue);
1754             }
1755             break;
1756
1757         default:
1758             noway_assert(!"expected a comparison operator");
1759     }
1760 }
1761
1762 void CodeGen::genJccLongLo(genTreeOps cmp, BasicBlock* jumpTrue, BasicBlock* jumpFalse)
1763 {
1764     switch (cmp)
1765     {
1766         case GT_EQ:
1767             inst_JMP(EJ_eq, jumpTrue);
1768             break;
1769
1770         case GT_NE:
1771             inst_JMP(EJ_ne, jumpTrue);
1772             break;
1773
1774         case GT_LT:
1775             inst_JMP(EJ_lo, jumpTrue);
1776             break;
1777
1778         case GT_LE:
1779             inst_JMP(EJ_ls, jumpTrue);
1780             break;
1781
1782         case GT_GE:
1783             inst_JMP(EJ_hs, jumpTrue);
1784             break;
1785
1786         case GT_GT:
1787             inst_JMP(EJ_hi, jumpTrue);
1788             break;
1789
1790         default:
1791             noway_assert(!"expected comparison");
1792     }
1793 }
1794
1795 //------------------------------------------------------------------------
1796 // genSetRegToCond: Generate code to materialize a condition into a register.
1797 //
1798 // Arguments:
1799 //   dstReg - The target register to set to 1 or 0
1800 //   tree - The GenTree Relop node that was used to set the Condition codes
1801 //
1802 // Return Value: none
1803 //
1804 // Preconditions:
1805 //    The condition codes must already have been appropriately set.
1806 //
1807 void CodeGen::genSetRegToCond(regNumber dstReg, GenTreePtr tree)
1808 {
1809     // Emit code like that:
1810     //   ...
1811     //   bgt True
1812     //   movs rD, #0
1813     //   b Next
1814     // True:
1815     //   movs rD, #1
1816     // Next:
1817     //   ...
1818
1819     CompareKind  compareKind = ((tree->gtFlags & GTF_UNSIGNED) != 0) ? CK_UNSIGNED : CK_SIGNED;
1820     emitJumpKind jmpKind     = genJumpKindForOper(tree->gtOper, compareKind);
1821
1822     BasicBlock* labelTrue = genCreateTempLabel();
1823     getEmitter()->emitIns_J(emitter::emitJumpKindToIns(jmpKind), labelTrue);
1824
1825     getEmitter()->emitIns_R_I(INS_mov, emitActualTypeSize(tree->gtType), dstReg, 0);
1826
1827     BasicBlock* labelNext = genCreateTempLabel();
1828     getEmitter()->emitIns_J(INS_b, labelNext);
1829
1830     genDefineTempLabel(labelTrue);
1831     getEmitter()->emitIns_R_I(INS_mov, emitActualTypeSize(tree->gtType), dstReg, 1);
1832     genDefineTempLabel(labelNext);
1833 }
1834
1835 //------------------------------------------------------------------------
1836 // genLongToIntCast: Generate code for long to int casts.
1837 //
1838 // Arguments:
1839 //    cast - The GT_CAST node
1840 //
1841 // Return Value:
1842 //    None.
1843 //
1844 // Assumptions:
1845 //    The cast node and its sources (via GT_LONG) must have been assigned registers.
1846 //    The destination cannot be a floating point type or a small integer type.
1847 //
1848 void CodeGen::genLongToIntCast(GenTree* cast)
1849 {
1850     assert(cast->OperGet() == GT_CAST);
1851
1852     GenTree* src = cast->gtGetOp1();
1853     noway_assert(src->OperGet() == GT_LONG);
1854
1855     genConsumeRegs(src);
1856
1857     var_types srcType  = ((cast->gtFlags & GTF_UNSIGNED) != 0) ? TYP_ULONG : TYP_LONG;
1858     var_types dstType  = cast->CastToType();
1859     regNumber loSrcReg = src->gtGetOp1()->gtRegNum;
1860     regNumber hiSrcReg = src->gtGetOp2()->gtRegNum;
1861     regNumber dstReg   = cast->gtRegNum;
1862
1863     assert((dstType == TYP_INT) || (dstType == TYP_UINT));
1864     assert(genIsValidIntReg(loSrcReg));
1865     assert(genIsValidIntReg(hiSrcReg));
1866     assert(genIsValidIntReg(dstReg));
1867
1868     if (cast->gtOverflow())
1869     {
1870         //
1871         // Generate an overflow check for [u]long to [u]int casts:
1872         //
1873         // long  -> int  - check if the upper 33 bits are all 0 or all 1
1874         //
1875         // ulong -> int  - check if the upper 33 bits are all 0
1876         //
1877         // long  -> uint - check if the upper 32 bits are all 0
1878         // ulong -> uint - check if the upper 32 bits are all 0
1879         //
1880
1881         if ((srcType == TYP_LONG) && (dstType == TYP_INT))
1882         {
1883             BasicBlock* allOne  = genCreateTempLabel();
1884             BasicBlock* success = genCreateTempLabel();
1885
1886             inst_RV_RV(INS_tst, loSrcReg, loSrcReg, TYP_INT, EA_4BYTE);
1887             emitJumpKind JmpNegative = genJumpKindForOper(GT_LT, CK_LOGICAL);
1888             inst_JMP(JmpNegative, allOne);
1889             inst_RV_RV(INS_tst, hiSrcReg, hiSrcReg, TYP_INT, EA_4BYTE);
1890             emitJumpKind jmpNotEqualL = genJumpKindForOper(GT_NE, CK_LOGICAL);
1891             genJumpToThrowHlpBlk(jmpNotEqualL, SCK_OVERFLOW);
1892             inst_JMP(EJ_jmp, success);
1893
1894             genDefineTempLabel(allOne);
1895             inst_RV_IV(INS_cmp, hiSrcReg, -1, EA_4BYTE);
1896             emitJumpKind jmpNotEqualS = genJumpKindForOper(GT_NE, CK_SIGNED);
1897             genJumpToThrowHlpBlk(jmpNotEqualS, SCK_OVERFLOW);
1898
1899             genDefineTempLabel(success);
1900         }
1901         else
1902         {
1903             if ((srcType == TYP_ULONG) && (dstType == TYP_INT))
1904             {
1905                 inst_RV_RV(INS_tst, loSrcReg, loSrcReg, TYP_INT, EA_4BYTE);
1906                 emitJumpKind JmpNegative = genJumpKindForOper(GT_LT, CK_LOGICAL);
1907                 genJumpToThrowHlpBlk(JmpNegative, SCK_OVERFLOW);
1908             }
1909
1910             inst_RV_RV(INS_tst, hiSrcReg, hiSrcReg, TYP_INT, EA_4BYTE);
1911             emitJumpKind jmpNotEqual = genJumpKindForOper(GT_NE, CK_LOGICAL);
1912             genJumpToThrowHlpBlk(jmpNotEqual, SCK_OVERFLOW);
1913         }
1914     }
1915
1916     if (dstReg != loSrcReg)
1917     {
1918         inst_RV_RV(INS_mov, dstReg, loSrcReg, TYP_INT, EA_4BYTE);
1919     }
1920
1921     genProduceReg(cast);
1922 }
1923
1924 //------------------------------------------------------------------------
1925 // genIntToFloatCast: Generate code to cast an int/long to float/double
1926 //
1927 // Arguments:
1928 //    treeNode - The GT_CAST node
1929 //
1930 // Return Value:
1931 //    None.
1932 //
1933 // Assumptions:
1934 //    Cast is a non-overflow conversion.
1935 //    The treeNode must have an assigned register.
1936 //    SrcType= int32/uint32/int64/uint64 and DstType=float/double.
1937 //
1938 void CodeGen::genIntToFloatCast(GenTreePtr treeNode)
1939 {
1940     // int --> float/double conversions are always non-overflow ones
1941     assert(treeNode->OperGet() == GT_CAST);
1942     assert(!treeNode->gtOverflow());
1943
1944     regNumber targetReg = treeNode->gtRegNum;
1945     assert(genIsValidFloatReg(targetReg));
1946
1947     GenTreePtr op1 = treeNode->gtOp.gtOp1;
1948     assert(!op1->isContained());             // Cannot be contained
1949     assert(genIsValidIntReg(op1->gtRegNum)); // Must be a valid int reg.
1950
1951     var_types dstType = treeNode->CastToType();
1952     var_types srcType = op1->TypeGet();
1953     assert(!varTypeIsFloating(srcType) && varTypeIsFloating(dstType));
1954
1955     // force the srcType to unsigned if GT_UNSIGNED flag is set
1956     if (treeNode->gtFlags & GTF_UNSIGNED)
1957     {
1958         srcType = genUnsignedType(srcType);
1959     }
1960
1961     // We should never see a srcType whose size is neither EA_4BYTE or EA_8BYTE
1962     // For conversions from small types (byte/sbyte/int16/uint16) to float/double,
1963     // we expect the front-end or lowering phase to have generated two levels of cast.
1964     //
1965     emitAttr srcSize = EA_ATTR(genTypeSize(srcType));
1966     noway_assert((srcSize == EA_4BYTE) || (srcSize == EA_8BYTE));
1967
1968     instruction insVcvt = INS_invalid;
1969
1970     if (dstType == TYP_DOUBLE)
1971     {
1972         if (srcSize == EA_4BYTE)
1973         {
1974             insVcvt = (varTypeIsUnsigned(srcType)) ? INS_vcvt_u2d : INS_vcvt_i2d;
1975         }
1976         else
1977         {
1978             assert(srcSize == EA_8BYTE);
1979             NYI_ARM("Casting int64/uint64 to double in genIntToFloatCast");
1980         }
1981     }
1982     else
1983     {
1984         assert(dstType == TYP_FLOAT);
1985         if (srcSize == EA_4BYTE)
1986         {
1987             insVcvt = (varTypeIsUnsigned(srcType)) ? INS_vcvt_u2f : INS_vcvt_i2f;
1988         }
1989         else
1990         {
1991             assert(srcSize == EA_8BYTE);
1992             NYI_ARM("Casting int64/uint64 to float in genIntToFloatCast");
1993         }
1994     }
1995
1996     genConsumeOperands(treeNode->AsOp());
1997
1998     assert(insVcvt != INS_invalid);
1999     getEmitter()->emitIns_R_R(INS_vmov_i2f, srcSize, treeNode->gtRegNum, op1->gtRegNum);
2000     getEmitter()->emitIns_R_R(insVcvt, srcSize, treeNode->gtRegNum, treeNode->gtRegNum);
2001
2002     genProduceReg(treeNode);
2003 }
2004
2005 //------------------------------------------------------------------------
2006 // genFloatToIntCast: Generate code to cast float/double to int/long
2007 //
2008 // Arguments:
2009 //    treeNode - The GT_CAST node
2010 //
2011 // Return Value:
2012 //    None.
2013 //
2014 // Assumptions:
2015 //    Cast is a non-overflow conversion.
2016 //    The treeNode must have an assigned register.
2017 //    SrcType=float/double and DstType= int32/uint32/int64/uint64
2018 //
2019 void CodeGen::genFloatToIntCast(GenTreePtr treeNode)
2020 {
2021     // we don't expect to see overflow detecting float/double --> int type conversions here
2022     // as they should have been converted into helper calls by front-end.
2023     assert(treeNode->OperGet() == GT_CAST);
2024     assert(!treeNode->gtOverflow());
2025
2026     regNumber targetReg = treeNode->gtRegNum;
2027     assert(genIsValidIntReg(targetReg)); // Must be a valid int reg.
2028
2029     GenTreePtr op1 = treeNode->gtOp.gtOp1;
2030     assert(!op1->isContained());               // Cannot be contained
2031     assert(genIsValidFloatReg(op1->gtRegNum)); // Must be a valid float reg.
2032
2033     var_types dstType = treeNode->CastToType();
2034     var_types srcType = op1->TypeGet();
2035     assert(varTypeIsFloating(srcType) && !varTypeIsFloating(dstType));
2036
2037     // We should never see a dstType whose size is neither EA_4BYTE or EA_8BYTE
2038     // For conversions to small types (byte/sbyte/int16/uint16) from float/double,
2039     // we expect the front-end or lowering phase to have generated two levels of cast.
2040     //
2041     emitAttr dstSize = EA_ATTR(genTypeSize(dstType));
2042     noway_assert((dstSize == EA_4BYTE) || (dstSize == EA_8BYTE));
2043
2044     instruction insVcvt = INS_invalid;
2045
2046     if (srcType == TYP_DOUBLE)
2047     {
2048         if (dstSize == EA_4BYTE)
2049         {
2050             insVcvt = (varTypeIsUnsigned(dstType)) ? INS_vcvt_d2u : INS_vcvt_d2i;
2051         }
2052         else
2053         {
2054             assert(dstSize == EA_8BYTE);
2055             NYI_ARM("Casting double to int64/uint64 in genIntToFloatCast");
2056         }
2057     }
2058     else
2059     {
2060         assert(srcType == TYP_FLOAT);
2061         if (dstSize == EA_4BYTE)
2062         {
2063             insVcvt = (varTypeIsUnsigned(dstType)) ? INS_vcvt_f2u : INS_vcvt_f2i;
2064         }
2065         else
2066         {
2067             assert(dstSize == EA_8BYTE);
2068             NYI_ARM("Casting float to int64/uint64 in genIntToFloatCast");
2069         }
2070     }
2071
2072     genConsumeOperands(treeNode->AsOp());
2073
2074     assert(insVcvt != INS_invalid);
2075     getEmitter()->emitIns_R_R(insVcvt, dstSize, op1->gtRegNum, op1->gtRegNum);
2076     getEmitter()->emitIns_R_R(INS_vmov_f2i, dstSize, treeNode->gtRegNum, op1->gtRegNum);
2077
2078     genProduceReg(treeNode);
2079 }
2080
2081 //------------------------------------------------------------------------
2082 // genEmitHelperCall: Emit a call to a helper function.
2083 //
2084 void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, regNumber callTargetReg /*= REG_NA */)
2085 {
2086     // Can we call the helper function directly
2087
2088     void *addr = NULL, **pAddr = NULL;
2089
2090 #if defined(DEBUG) && defined(PROFILING_SUPPORTED)
2091     // Don't ask VM if it hasn't requested ELT hooks
2092     if (!compiler->compProfilerHookNeeded && compiler->opts.compJitELTHookEnabled &&
2093         (helper == CORINFO_HELP_PROF_FCN_ENTER || helper == CORINFO_HELP_PROF_FCN_LEAVE ||
2094          helper == CORINFO_HELP_PROF_FCN_TAILCALL))
2095     {
2096         addr = compiler->compProfilerMethHnd;
2097     }
2098     else
2099 #endif
2100     {
2101         addr = compiler->compGetHelperFtn((CorInfoHelpFunc)helper, (void**)&pAddr);
2102     }
2103
2104     if (!addr || !arm_Valid_Imm_For_BL((ssize_t)addr))
2105     {
2106         if (callTargetReg == REG_NA)
2107         {
2108             // If a callTargetReg has not been explicitly provided, we will use REG_DEFAULT_HELPER_CALL_TARGET, but
2109             // this is only a valid assumption if the helper call is known to kill REG_DEFAULT_HELPER_CALL_TARGET.
2110             callTargetReg = REG_DEFAULT_HELPER_CALL_TARGET;
2111         }
2112
2113         // Load the address into a register and call through a register
2114         if (addr)
2115         {
2116             instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, callTargetReg, (ssize_t)addr);
2117         }
2118         else
2119         {
2120             getEmitter()->emitIns_R_AI(INS_ldr, EA_PTR_DSP_RELOC, callTargetReg, (ssize_t)pAddr);
2121             regTracker.rsTrackRegTrash(callTargetReg);
2122         }
2123
2124         getEmitter()->emitIns_Call(emitter::EC_INDIR_R, compiler->eeFindHelper(helper),
2125                                    INDEBUG_LDISASM_COMMA(nullptr) NULL, // addr
2126                                    argSize, retSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur,
2127                                    gcInfo.gcRegByrefSetCur,
2128                                    BAD_IL_OFFSET, // ilOffset
2129                                    callTargetReg, // ireg
2130                                    REG_NA, 0, 0,  // xreg, xmul, disp
2131                                    false,         // isJump
2132                                    emitter::emitNoGChelper(helper),
2133                                    (CorInfoHelpFunc)helper == CORINFO_HELP_PROF_FCN_LEAVE);
2134     }
2135     else
2136     {
2137         getEmitter()->emitIns_Call(emitter::EC_FUNC_TOKEN, compiler->eeFindHelper(helper),
2138                                    INDEBUG_LDISASM_COMMA(nullptr) addr, argSize, retSize, gcInfo.gcVarPtrSetCur,
2139                                    gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, BAD_IL_OFFSET, REG_NA, REG_NA, 0,
2140                                    0,     /* ilOffset, ireg, xreg, xmul, disp */
2141                                    false, /* isJump */
2142                                    emitter::emitNoGChelper(helper),
2143                                    (CorInfoHelpFunc)helper == CORINFO_HELP_PROF_FCN_LEAVE);
2144     }
2145
2146     regTracker.rsTrashRegSet(RBM_CALLEE_TRASH);
2147     regTracker.rsTrashRegsForGCInterruptability();
2148 }
2149
2150 //------------------------------------------------------------------------
2151 // genStoreLongLclVar: Generate code to store a non-enregistered long lclVar
2152 //
2153 // Arguments:
2154 //    treeNode - A TYP_LONG lclVar node.
2155 //
2156 // Return Value:
2157 //    None.
2158 //
2159 // Assumptions:
2160 //    'treeNode' must be a TYP_LONG lclVar node for a lclVar that has NOT been promoted.
2161 //    Its operand must be a GT_LONG node.
2162 //
2163 void CodeGen::genStoreLongLclVar(GenTree* treeNode)
2164 {
2165     emitter* emit = getEmitter();
2166
2167     GenTreeLclVarCommon* lclNode = treeNode->AsLclVarCommon();
2168     unsigned             lclNum  = lclNode->gtLclNum;
2169     LclVarDsc*           varDsc  = &(compiler->lvaTable[lclNum]);
2170     assert(varDsc->TypeGet() == TYP_LONG);
2171     assert(!varDsc->lvPromoted);
2172     GenTreePtr op1 = treeNode->gtOp.gtOp1;
2173     noway_assert(op1->OperGet() == GT_LONG || op1->OperGet() == GT_MUL_LONG);
2174     genConsumeRegs(op1);
2175
2176     if (op1->OperGet() == GT_LONG)
2177     {
2178         // Definitions of register candidates will have been lowered to 2 int lclVars.
2179         assert(!treeNode->InReg());
2180
2181         GenTreePtr loVal = op1->gtGetOp1();
2182         GenTreePtr hiVal = op1->gtGetOp2();
2183
2184         // NYI: Contained immediates.
2185         NYI_IF((loVal->gtRegNum == REG_NA) || (hiVal->gtRegNum == REG_NA),
2186                "Store of long lclVar with contained immediate");
2187
2188         emit->emitIns_S_R(ins_Store(TYP_INT), EA_4BYTE, loVal->gtRegNum, lclNum, 0);
2189         emit->emitIns_S_R(ins_Store(TYP_INT), EA_4BYTE, hiVal->gtRegNum, lclNum, genTypeSize(TYP_INT));
2190     }
2191     else if (op1->OperGet() == GT_MUL_LONG)
2192     {
2193         assert((op1->gtFlags & GTF_MUL_64RSLT) != 0);
2194
2195         // Stack store
2196         getEmitter()->emitIns_S_R(ins_Store(TYP_INT), emitTypeSize(TYP_INT), REG_LNGRET_LO, lclNum, 0);
2197         getEmitter()->emitIns_S_R(ins_Store(TYP_INT), emitTypeSize(TYP_INT), REG_LNGRET_HI, lclNum,
2198                                   genTypeSize(TYP_INT));
2199     }
2200 }
2201
2202 #endif // _TARGET_ARM_
2203
2204 #endif // !LEGACY_BACKEND