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.
5 /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
8 XX ARM Code Generator XX
10 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
11 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
18 #ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator
26 //------------------------------------------------------------------------
27 // genCallFinally: Generate a call to the finally block.
29 BasicBlock* CodeGen::genCallFinally(BasicBlock* block)
31 BasicBlock* bbFinallyRet = nullptr;
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());
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);
42 bbFinallyRet = block->bbNext->bbJumpDest;
43 bbFinallyRet->bbFlags |= BBF_JMP_TARGET;
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);
50 // Jump to the finally BB
51 inst_JMP(EJ_jmp, block->bbJumpDest);
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
57 assert(!(block->bbFlags & BBF_RETLESS_CALL));
58 assert(block->isBBCallAlwaysPair());
62 //------------------------------------------------------------------------
64 void CodeGen::genEHCatchRet(BasicBlock* block)
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);
70 //------------------------------------------------------------------------
71 // instGen_Set_Reg_To_Imm: Move an immediate value into an integer register.
73 void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size, regNumber reg, ssize_t imm, insFlags flags)
75 // reg cannot be a FP register
76 assert(!genIsValidFloatReg(reg));
78 if (!compiler->opts.compReloc)
80 size = EA_SIZE(size); // Strip any Reloc flags from size if we aren't doing relocs
83 if (EA_IS_RELOC(size))
85 getEmitter()->emitIns_R_I(INS_movw, size, reg, imm);
86 getEmitter()->emitIns_R_I(INS_movt, size, reg, imm);
90 instGen_Set_Reg_To_Zero(size, reg, flags);
94 if (arm_Valid_Imm_For_Mov(imm))
96 getEmitter()->emitIns_R_I(INS_mov, size, reg, imm, flags);
98 else // We have to use a movw/movt pair of instructions
100 ssize_t imm_lo16 = (imm & 0xffff);
101 ssize_t imm_hi16 = (imm >> 16) & 0xffff;
103 assert(arm_Valid_Imm_For_Mov(imm_lo16));
104 assert(imm_hi16 != 0);
106 getEmitter()->emitIns_R_I(INS_movw, size, reg, imm_lo16);
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.
113 if (getEmitter()->isLowRegister(reg) && (imm_hi16 == 0xffff) && ((imm_lo16 & 0x8000) == 0x8000))
115 getEmitter()->emitIns_R_R(INS_sxth, EA_2BYTE, reg, reg);
119 getEmitter()->emitIns_R_I(INS_movt, size, reg, imm_hi16);
122 if (flags == INS_FLAGS_SET)
123 getEmitter()->emitIns_R_R(INS_mov, size, reg, reg, INS_FLAGS_SET);
127 regTracker.rsTrackRegIntCns(reg, imm);
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'.
135 // This does not call genProduceReg() on the target register.
137 void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTreePtr tree)
139 switch (tree->gtOper)
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();
148 bool needReloc = compiler->opts.compReloc && tree->IsIconHandle();
151 instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, targetReg, cnsVal);
152 regTracker.rsTrackRegTrash(targetReg);
156 genSetRegToIcon(targetReg, cnsVal, targetType);
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)
168 // Get a temp integer register
169 regNumber tmpReg = tree->GetSingleTempReg();
171 float f = forceCastToFloat(constValue);
172 genSetRegToIcon(tmpReg, *((int*)(&f)));
173 getEmitter()->emitIns_R_R(INS_vmov_i2f, EA_4BYTE, targetReg, tmpReg);
177 assert(targetType == TYP_DOUBLE);
179 unsigned* cv = (unsigned*)&constValue;
181 // Get two temp integer registers
182 regNumber tmpReg1 = tree->ExtractTempReg();
183 regNumber tmpReg2 = tree->GetSingleTempReg();
185 genSetRegToIcon(tmpReg1, cv[0]);
186 genSetRegToIcon(tmpReg2, cv[1]);
188 getEmitter()->emitIns_R_R_R(INS_vmov_i2d, EA_8BYTE, targetReg, tmpReg1, tmpReg2);
198 //------------------------------------------------------------------------
199 // genCodeForBinary: Generate code for many binary arithmetic operators
200 // This method is expected to have called genConsumeOperands() before calling it.
203 // treeNode - The binary operation for which we are generating code.
209 // Mul and div are not handled here.
210 // See the assert below for the operators that are handled.
212 void CodeGen::genCodeForBinary(GenTree* treeNode)
214 const genTreeOps oper = treeNode->OperGet();
215 regNumber targetReg = treeNode->gtRegNum;
216 var_types targetType = treeNode->TypeGet();
217 emitter* emit = getEmitter();
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);
222 GenTreePtr op1 = treeNode->gtGetOp1();
223 GenTreePtr op2 = treeNode->gtGetOp2();
225 instruction ins = genGetInsForOper(oper, targetType);
227 // The arithmetic node must be sitting in a register (since it's not contained)
228 noway_assert(targetReg != REG_NA);
230 if ((oper == GT_ADD_LO || oper == GT_SUB_LO))
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,
239 regNumber r = emit->emitInsTernary(ins, emitTypeSize(treeNode), treeNode, op1, op2);
240 assert(r == targetReg);
243 genProduceReg(treeNode);
246 //------------------------------------------------------------------------
247 // genReturn: Generates code for return statement.
248 // In case of struct return, delegates to the genStructReturn method.
251 // treeNode - The GT_RETURN or GT_RETFILT tree node.
256 void CodeGen::genReturn(GenTreePtr treeNode)
258 assert(treeNode->OperGet() == GT_RETURN || treeNode->OperGet() == GT_RETFILT);
259 GenTreePtr op1 = treeNode->gtGetOp1();
260 var_types targetType = treeNode->TypeGet();
262 // A void GT_RETFILT is the end of a finally. For non-void filter returns we need to load the result in the return
263 // register, if it's not already there. The processing is the same as GT_RETURN. For filters, the IL spec says the
264 // result is type int32. Further, the only legal values are 0 or 1; the use of other values is "undefined".
265 assert(!treeNode->OperIs(GT_RETFILT) || (targetType == TYP_VOID) || (targetType == TYP_INT));
268 if (targetType == TYP_VOID)
270 assert(op1 == nullptr);
274 if (treeNode->TypeGet() == TYP_LONG)
276 assert(op1 != nullptr);
277 noway_assert(op1->OperGet() == GT_LONG);
278 GenTree* loRetVal = op1->gtGetOp1();
279 GenTree* hiRetVal = op1->gtGetOp2();
280 noway_assert((loRetVal->gtRegNum != REG_NA) && (hiRetVal->gtRegNum != REG_NA));
282 genConsumeReg(loRetVal);
283 genConsumeReg(hiRetVal);
284 if (loRetVal->gtRegNum != REG_LNGRET_LO)
286 inst_RV_RV(ins_Copy(targetType), REG_LNGRET_LO, loRetVal->gtRegNum, TYP_INT);
288 if (hiRetVal->gtRegNum != REG_LNGRET_HI)
290 inst_RV_RV(ins_Copy(targetType), REG_LNGRET_HI, hiRetVal->gtRegNum, TYP_INT);
295 if (varTypeIsStruct(treeNode))
297 NYI_ARM("struct return");
299 else if (targetType != TYP_VOID)
301 assert(op1 != nullptr);
302 noway_assert(op1->gtRegNum != REG_NA);
304 // !! NOTE !! genConsumeReg will clear op1 as GC ref after it has
305 // consumed a reg for the operand. This is because the variable
306 // is dead after return. But we are issuing more instructions
307 // like "profiler leave callback" after this consumption. So
308 // if you are issuing more instructions after this point,
309 // remember to keep the variable live up until the new method
310 // exit point where it is actually dead.
313 regNumber retReg = varTypeIsFloating(treeNode) ? REG_FLOATRET : REG_INTRET;
314 if (op1->gtRegNum != retReg)
316 inst_RV_RV(ins_Move_Extend(targetType, true), retReg, op1->gtRegNum, targetType);
322 //------------------------------------------------------------------------
323 // genLockedInstructions: Generate code for the locked operations.
326 // Handles GT_LOCKADD, GT_XCHG, GT_XADD nodes.
328 void CodeGen::genLockedInstructions(GenTreeOp* treeNode)
330 NYI("genLockedInstructions");
333 //--------------------------------------------------------------------------------------
334 // genLclHeap: Generate code for localloc
337 // There are 2 ways depending from build version to generate code for localloc:
338 // 1) For debug build where memory should be initialized we generate loop
339 // which invoke push {tmpReg} N times.
340 // 2) Fore /o build However, we tickle the pages to ensure that SP is always
341 // valid and is in sync with the "stack guard page". Amount of iteration
345 // There can be some optimization:
346 // 1) It's not needed to generate loop for zero size allocation
347 // 2) For small allocation (less than 4 store) we unroll loop
348 // 3) For allocation less than PAGE_SIZE and when it's not needed to initialize
349 // memory to zero, we can just increment SP.
351 // Notes: Size N should be aligned to STACK_ALIGN before any allocation
353 void CodeGen::genLclHeap(GenTreePtr tree)
355 assert(tree->OperGet() == GT_LCLHEAP);
357 GenTreePtr size = tree->gtOp.gtOp1;
358 noway_assert((genActualType(size->gtType) == TYP_INT) || (genActualType(size->gtType) == TYP_I_IMPL));
360 // Result of localloc will be returned in regCnt.
361 // Also it used as temporary register in code generation
362 // for storing allocation size
363 regNumber regCnt = tree->gtRegNum;
364 regNumber pspSymReg = REG_NA;
365 var_types type = genActualType(size->gtType);
366 emitAttr easz = emitTypeSize(type);
367 BasicBlock* endLabel = nullptr;
368 BasicBlock* loop = nullptr;
369 unsigned stackAdjustment = 0;
373 if (compiler->opts.compStackCheckOnRet)
375 noway_assert(compiler->lvaReturnEspCheck != 0xCCCCCCCC &&
376 compiler->lvaTable[compiler->lvaReturnEspCheck].lvDoNotEnregister &&
377 compiler->lvaTable[compiler->lvaReturnEspCheck].lvOnFrame);
378 getEmitter()->emitIns_S_R(INS_cmp, EA_PTRSIZE, REG_SPBASE, compiler->lvaReturnEspCheck, 0);
380 BasicBlock* esp_check = genCreateTempLabel();
381 emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED);
382 inst_JMP(jmpEqual, esp_check);
383 getEmitter()->emitIns(INS_BREAKPOINT);
384 genDefineTempLabel(esp_check);
388 noway_assert(isFramePointerUsed()); // localloc requires Frame Pointer to be established since SP changes
389 noway_assert(genStackLevel == 0); // Can't have anything on the stack
391 // Whether method has PSPSym.
393 #if FEATURE_EH_FUNCLETS
394 hasPspSym = (compiler->lvaPSPSym != BAD_VAR_NUM);
399 // Check to 0 size allocations
400 // size_t amount = 0;
401 if (size->IsCnsIntOrI())
403 // If size is a constant, then it must be contained.
404 assert(size->isContained());
406 // If amount is zero then return null in regCnt
407 size_t amount = size->gtIntCon.gtIconVal;
410 instGen_Set_Reg_To_Zero(EA_PTRSIZE, regCnt);
416 // If 0 bail out by returning null in regCnt
417 genConsumeRegAndCopy(size, regCnt);
418 endLabel = genCreateTempLabel();
419 getEmitter()->emitIns_R_R(INS_TEST, easz, regCnt, regCnt);
420 emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED);
421 inst_JMP(jmpEqual, endLabel);
425 #if FEATURE_EH_FUNCLETS
426 // If we have PSPsym, then need to re-locate it after localloc.
429 stackAdjustment += STACK_ALIGN;
431 // Save a copy of PSPSym
432 pspSymReg = tree->ExtractTempReg();
433 getEmitter()->emitIns_R_S(ins_Load(TYP_I_IMPL), EA_PTRSIZE, pspSymReg, compiler->lvaPSPSym, 0);
437 #if FEATURE_FIXED_OUT_ARGS
438 // If we have an outgoing arg area then we must adjust the SP by popping off the
439 // outgoing arg area. We will restore it right before we return from this method.
440 if (compiler->lvaOutgoingArgSpaceSize > 0)
442 assert((compiler->lvaOutgoingArgSpaceSize % STACK_ALIGN) == 0); // This must be true for the stack to remain
444 inst_RV_IV(INS_add, REG_SPBASE, compiler->lvaOutgoingArgSpaceSize, EA_PTRSIZE);
445 stackAdjustment += compiler->lvaOutgoingArgSpaceSize;
449 // Put aligned allocation size to regCnt
450 if (size->IsCnsIntOrI())
452 // 'amount' is the total number of bytes to localloc to properly STACK_ALIGN
453 size_t amount = size->gtIntCon.gtIconVal;
454 amount = AlignUp(amount, STACK_ALIGN);
456 // For small allocations we will generate up to four stp instructions
457 size_t cntStackAlignedWidthItems = (amount >> STACK_ALIGN_SHIFT);
458 if (cntStackAlignedWidthItems <= 4)
460 instGen_Set_Reg_To_Zero(EA_PTRSIZE, regCnt);
462 while (cntStackAlignedWidthItems != 0)
464 inst_IV(INS_push, (unsigned)genRegMask(regCnt));
465 cntStackAlignedWidthItems -= 1;
470 else if (!compiler->info.compInitMem && (amount < compiler->eeGetPageSize())) // must be < not <=
472 // Since the size is a page or less, simply adjust the SP value
473 // The SP might already be in the guard page, must touch it BEFORE
474 // the alloc, not after.
475 getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, regCnt, REG_SP, 0);
476 inst_RV_IV(INS_sub, REG_SP, amount, EA_PTRSIZE);
480 // regCnt will be the total number of bytes to locAlloc
481 genSetRegToIcon(regCnt, amount, ((int)amount == amount) ? TYP_INT : TYP_LONG);
485 // Round up the number of bytes to allocate to a STACK_ALIGN boundary.
486 inst_RV_IV(INS_add, regCnt, (STACK_ALIGN - 1), emitActualTypeSize(type));
487 inst_RV_IV(INS_AND, regCnt, ~(STACK_ALIGN - 1), emitActualTypeSize(type));
491 if (compiler->info.compInitMem)
493 // At this point 'regCnt' is set to the total number of bytes to locAlloc.
494 // Since we have to zero out the allocated memory AND ensure that RSP is always valid
495 // by tickling the pages, we will just push 0's on the stack.
497 regNumber regTmp = tree->ExtractTempReg();
498 instGen_Set_Reg_To_Zero(EA_PTRSIZE, regTmp);
501 BasicBlock* loop = genCreateTempLabel();
502 genDefineTempLabel(loop);
504 noway_assert(STACK_ALIGN == 8);
505 inst_IV(INS_push, (unsigned)genRegMask(regTmp));
506 inst_IV(INS_push, (unsigned)genRegMask(regTmp));
509 // Note that regCnt is the number of bytes to stack allocate.
510 assert(genIsValidIntReg(regCnt));
511 getEmitter()->emitIns_R_I(INS_sub, EA_PTRSIZE, regCnt, STACK_ALIGN, INS_FLAGS_SET);
512 emitJumpKind jmpNotEqual = genJumpKindForOper(GT_NE, CK_SIGNED);
513 inst_JMP(jmpNotEqual, loop);
517 // At this point 'regCnt' is set to the total number of bytes to locAlloc.
519 // We don't need to zero out the allocated memory. However, we do have
520 // to tickle the pages to ensure that SP is always valid and is
521 // in sync with the "stack guard page". Note that in the worst
522 // case SP is on the last byte of the guard page. Thus you must
523 // touch SP+0 first not SP+0x1000.
525 // Another subtlety is that you don't want SP to be exactly on the
526 // boundary of the guard page because PUSH is predecrement, thus
527 // call setup would not touch the guard page but just beyond it
529 // Note that we go through a few hoops so that SP never points to
530 // illegal pages at any time during the ticking process
532 // subs regCnt, SP, regCnt // regCnt now holds ultimate SP
533 // jb Loop // result is smaller than orignial SP (no wrap around)
534 // mov regCnt, #0 // Overflow, pick lowest possible value
537 // ldr regTmp, [SP + 0] // tickle the page - read from the page
538 // sub regTmp, SP, PAGE_SIZE // decrement SP by PAGE_SIZE
539 // cmp regTmp, regCnt
549 regNumber regTmp = tree->ExtractTempReg();
551 BasicBlock* loop = genCreateTempLabel();
552 BasicBlock* done = genCreateTempLabel();
554 // subs regCnt, SP, regCnt // regCnt now holds ultimate SP
555 getEmitter()->emitIns_R_R_R(INS_sub, EA_PTRSIZE, regCnt, REG_SPBASE, regCnt, INS_FLAGS_SET);
557 inst_JMP(EJ_vc, loop); // branch if the V flag is not set
559 // Ups... Overflow, set regCnt to lowest possible value
560 instGen_Set_Reg_To_Zero(EA_PTRSIZE, regCnt);
562 genDefineTempLabel(loop);
564 // tickle the page - Read from the updated SP - this triggers a page fault when on the guard page
565 getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, regTmp, REG_SPBASE, 0);
567 // decrement SP by PAGE_SIZE
568 getEmitter()->emitIns_R_R_I(INS_sub, EA_PTRSIZE, regTmp, REG_SPBASE, compiler->eeGetPageSize());
570 getEmitter()->emitIns_R_R(INS_cmp, EA_PTRSIZE, regTmp, regCnt);
571 emitJumpKind jmpLTU = genJumpKindForOper(GT_LT, CK_UNSIGNED);
572 inst_JMP(jmpLTU, done);
574 // Update SP to be at the next page of stack that we will tickle
575 getEmitter()->emitIns_R_R(INS_mov, EA_PTRSIZE, REG_SPBASE, regCnt);
577 // Jump to loop and tickle new stack address
578 inst_JMP(EJ_jmp, loop);
580 // Done with stack tickle loop
581 genDefineTempLabel(done);
583 // Now just move the final value to SP
584 getEmitter()->emitIns_R_R(INS_mov, EA_PTRSIZE, REG_SPBASE, regCnt);
588 // Re-adjust SP to allocate PSPSym and out-going arg area
589 if (stackAdjustment != 0)
591 assert((stackAdjustment % STACK_ALIGN) == 0); // This must be true for the stack to remain aligned
592 assert(stackAdjustment > 0);
593 getEmitter()->emitIns_R_R_I(INS_sub, EA_PTRSIZE, REG_SPBASE, REG_SPBASE, (int)stackAdjustment);
595 #if FEATURE_EH_FUNCLETS
596 // Write PSPSym to its new location.
599 assert(genIsValidIntReg(pspSymReg));
600 getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, pspSymReg, compiler->lvaPSPSym, 0);
603 // Return the stackalloc'ed address in result register.
604 // regCnt = RSP + stackAdjustment.
605 getEmitter()->emitIns_R_R_I(INS_add, EA_PTRSIZE, regCnt, REG_SPBASE, (int)stackAdjustment);
607 else // stackAdjustment == 0
609 // Move the final value of SP to regCnt
610 inst_RV_RV(INS_mov, regCnt, REG_SPBASE);
614 if (endLabel != nullptr)
615 genDefineTempLabel(endLabel);
617 // Write the lvaLocAllocSPvar stack frame slot
618 if (compiler->lvaLocAllocSPvar != BAD_VAR_NUM)
620 getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, regCnt, compiler->lvaLocAllocSPvar, 0);
624 if (compiler->opts.compNeedStackProbes)
626 genGenerateStackProbe();
632 if (compiler->opts.compStackCheckOnRet)
634 noway_assert(compiler->lvaReturnEspCheck != 0xCCCCCCCC &&
635 compiler->lvaTable[compiler->lvaReturnEspCheck].lvDoNotEnregister &&
636 compiler->lvaTable[compiler->lvaReturnEspCheck].lvOnFrame);
637 getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, regCnt, compiler->lvaReturnEspCheck, 0);
644 //------------------------------------------------------------------------
645 // genTableBasedSwitch: generate code for a switch statement based on a table of ip-relative offsets
647 void CodeGen::genTableBasedSwitch(GenTree* treeNode)
649 genConsumeOperands(treeNode->AsOp());
650 regNumber idxReg = treeNode->gtOp.gtOp1->gtRegNum;
651 regNumber baseReg = treeNode->gtOp.gtOp2->gtRegNum;
653 getEmitter()->emitIns_R_ARX(INS_ldr, EA_4BYTE, REG_PC, baseReg, idxReg, TARGET_POINTER_SIZE, 0);
656 //------------------------------------------------------------------------
657 // genJumpTable: emits the table and an instruction to get the address of the first element
659 void CodeGen::genJumpTable(GenTree* treeNode)
661 noway_assert(compiler->compCurBB->bbJumpKind == BBJ_SWITCH);
662 assert(treeNode->OperGet() == GT_JMPTABLE);
664 unsigned jumpCount = compiler->compCurBB->bbJumpSwt->bbsCount;
665 BasicBlock** jumpTable = compiler->compCurBB->bbJumpSwt->bbsDstTab;
668 jmpTabBase = getEmitter()->emitBBTableDataGenBeg(jumpCount, false);
670 JITDUMP("\n J_M%03u_DS%02u LABEL DWORD\n", Compiler::s_compMethodsCount, jmpTabBase);
672 for (unsigned i = 0; i < jumpCount; i++)
674 BasicBlock* target = *jumpTable++;
675 noway_assert(target->bbFlags & BBF_JMP_TARGET);
677 JITDUMP(" DD L_M%03u_BB%02u\n", Compiler::s_compMethodsCount, target->bbNum);
679 getEmitter()->emitDataGenData(i, target);
682 getEmitter()->emitDataGenEnd();
684 getEmitter()->emitIns_R_D(INS_movw, EA_HANDLE_CNS_RELOC, jmpTabBase, treeNode->gtRegNum);
685 getEmitter()->emitIns_R_D(INS_movt, EA_HANDLE_CNS_RELOC, jmpTabBase, treeNode->gtRegNum);
687 genProduceReg(treeNode);
690 //------------------------------------------------------------------------
691 // genGetInsForOper: Return instruction encoding of the operation tree.
693 instruction CodeGen::genGetInsForOper(genTreeOps oper, var_types type)
697 if (varTypeIsFloating(type))
698 return CodeGen::ins_MathOp(oper, type);
715 ins = INS_SHIFT_LEFT_LOGICAL;
727 ins = INS_SHIFT_RIGHT_ARITHM;
730 ins = INS_SHIFT_RIGHT_LOGICAL;
754 ins = INS_SHIFT_LEFT_LOGICAL;
757 ins = INS_SHIFT_RIGHT_LOGICAL;
766 // Generates CpBlk code by performing a loop unroll
768 // The size argument of the CpBlk node is a constant and <= 64 bytes.
769 // This may seem small but covers >95% of the cases in several framework assemblies.
770 void CodeGen::genCodeForCpBlkUnroll(GenTreeBlk* cpBlkNode)
772 NYI_ARM("genCodeForCpBlkUnroll");
775 // Generate code for InitBlk by performing a loop unroll
777 // a) Both the size and fill byte value are integer constants.
778 // b) The size of the struct to initialize is smaller than INITBLK_UNROLL_LIMIT bytes.
779 void CodeGen::genCodeForInitBlkUnroll(GenTreeBlk* initBlkNode)
781 NYI_ARM("genCodeForInitBlkUnroll");
784 //------------------------------------------------------------------------
785 // genCodeForNegNot: Produce code for a GT_NEG/GT_NOT node.
790 void CodeGen::genCodeForNegNot(GenTree* tree)
792 assert(tree->OperIs(GT_NEG, GT_NOT));
794 var_types targetType = tree->TypeGet();
796 assert(!tree->OperIs(GT_NOT) || !varTypeIsFloating(targetType));
798 regNumber targetReg = tree->gtRegNum;
799 instruction ins = genGetInsForOper(tree->OperGet(), targetType);
801 // The arithmetic node must be sitting in a register (since it's not contained)
802 assert(!tree->isContained());
803 // The dst can only be a register.
804 assert(targetReg != REG_NA);
806 GenTreePtr operand = tree->gtGetOp1();
807 assert(!operand->isContained());
808 // The src must be a register.
809 regNumber operandReg = genConsumeReg(operand);
813 getEmitter()->emitIns_R_R(ins, emitTypeSize(tree), targetReg, operandReg);
817 getEmitter()->emitIns_R_R_I(ins, emitTypeSize(tree), targetReg, operandReg, 0);
823 // Generate code for CpObj nodes wich copy structs that have interleaved
825 // For this case we'll generate a sequence of loads/stores in the case of struct
826 // slots that don't contain GC pointers. The generated code will look like:
827 // ldr tempReg, [R13, #8]
828 // str tempReg, [R14, #8]
830 // In the case of a GC-Pointer we'll call the ByRef write barrier helper
831 // who happens to use the same registers as the previous call to maintain
832 // the same register requirements and register killsets:
833 // bl CORINFO_HELP_ASSIGN_BYREF
835 // So finally an example would look like this:
836 // ldr tempReg, [R13, #8]
837 // str tempReg, [R14, #8]
838 // bl CORINFO_HELP_ASSIGN_BYREF
839 // ldr tempReg, [R13, #8]
840 // str tempReg, [R14, #8]
841 // bl CORINFO_HELP_ASSIGN_BYREF
842 // ldr tempReg, [R13, #8]
843 // str tempReg, [R14, #8]
844 void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode)
846 GenTreePtr dstAddr = cpObjNode->Addr();
847 GenTreePtr source = cpObjNode->Data();
848 var_types srcAddrType = TYP_BYREF;
849 bool sourceIsLocal = false;
850 regNumber dstReg = REG_NA;
851 regNumber srcReg = REG_NA;
853 assert(source->isContained());
854 if (source->gtOper == GT_IND)
856 GenTree* srcAddr = source->gtGetOp1();
857 assert(!srcAddr->isContained());
858 srcAddrType = srcAddr->TypeGet();
862 noway_assert(source->IsLocal());
863 sourceIsLocal = true;
866 bool dstOnStack = dstAddr->OperIsLocalAddr();
869 assert(!dstAddr->isContained());
871 // This GenTree node has data about GC pointers, this means we're dealing
873 assert(cpObjNode->gtGcPtrCount > 0);
876 // Consume the operands and get them into the right registers.
877 // They may now contain gc pointers (depending on their type; gcMarkRegPtrVal will "do the right thing").
878 genConsumeBlockOp(cpObjNode, REG_WRITE_BARRIER_DST_BYREF, REG_WRITE_BARRIER_SRC_BYREF, REG_NA);
879 gcInfo.gcMarkRegPtrVal(REG_WRITE_BARRIER_SRC_BYREF, srcAddrType);
880 gcInfo.gcMarkRegPtrVal(REG_WRITE_BARRIER_DST_BYREF, dstAddr->TypeGet());
882 // Temp register used to perform the sequence of loads and stores.
883 regNumber tmpReg = cpObjNode->ExtractTempReg();
884 assert(genIsValidIntReg(tmpReg));
886 unsigned slots = cpObjNode->gtSlots;
887 emitter* emit = getEmitter();
889 BYTE* gcPtrs = cpObjNode->gtGcPtrs;
891 // If we can prove it's on the stack we don't need to use the write barrier.
892 emitAttr attr = EA_PTRSIZE;
895 for (unsigned i = 0; i < slots; ++i)
897 if (gcPtrs[i] == GCT_GCREF)
899 else if (gcPtrs[i] == GCT_BYREF)
901 emit->emitIns_R_R_I(INS_ldr, attr, tmpReg, REG_WRITE_BARRIER_SRC_BYREF, TARGET_POINTER_SIZE,
902 INS_FLAGS_DONT_CARE, INS_OPTS_LDST_POST_INC);
903 emit->emitIns_R_R_I(INS_str, attr, tmpReg, REG_WRITE_BARRIER_DST_BYREF, TARGET_POINTER_SIZE,
904 INS_FLAGS_DONT_CARE, INS_OPTS_LDST_POST_INC);
909 unsigned gcPtrCount = cpObjNode->gtGcPtrCount;
917 emit->emitIns_R_R_I(INS_ldr, attr, tmpReg, REG_WRITE_BARRIER_SRC_BYREF, TARGET_POINTER_SIZE,
918 INS_FLAGS_DONT_CARE, INS_OPTS_LDST_POST_INC);
919 emit->emitIns_R_R_I(INS_str, attr, tmpReg, REG_WRITE_BARRIER_DST_BYREF, TARGET_POINTER_SIZE,
920 INS_FLAGS_DONT_CARE, INS_OPTS_LDST_POST_INC);
924 // In the case of a GC-Pointer we'll call the ByRef write barrier helper
925 genEmitHelperCall(CORINFO_HELP_ASSIGN_BYREF, 0, EA_PTRSIZE);
932 assert(gcPtrCount == 0);
935 // Clear the gcInfo for registers of source and dest.
936 // While we normally update GC info prior to the last instruction that uses them,
937 // these actually live into the helper call.
938 gcInfo.gcMarkRegSetNpt(RBM_WRITE_BARRIER_SRC_BYREF | RBM_WRITE_BARRIER_DST_BYREF);
941 //------------------------------------------------------------------------
942 // genCodeForShiftLong: Generates the code sequence for a GenTree node that
943 // represents a three operand bit shift or rotate operation (<<Hi, >>Lo).
946 // tree - the bit shift node (that specifies the type of bit shift to perform).
949 // a) All GenTrees are register allocated.
950 // b) The shift-by-amount in tree->gtOp.gtOp2 is a contained constant
952 void CodeGen::genCodeForShiftLong(GenTreePtr tree)
954 // Only the non-RMW case here.
955 genTreeOps oper = tree->OperGet();
956 assert(oper == GT_LSH_HI || oper == GT_RSH_LO);
958 GenTree* operand = tree->gtOp.gtOp1;
959 assert(operand->OperGet() == GT_LONG);
960 assert(operand->gtOp.gtOp1->isUsedFromReg());
961 assert(operand->gtOp.gtOp2->isUsedFromReg());
963 GenTree* operandLo = operand->gtGetOp1();
964 GenTree* operandHi = operand->gtGetOp2();
966 regNumber regLo = operandLo->gtRegNum;
967 regNumber regHi = operandHi->gtRegNum;
969 genConsumeOperands(tree->AsOp());
971 var_types targetType = tree->TypeGet();
972 instruction ins = genGetInsForOper(oper, targetType);
974 GenTreePtr shiftBy = tree->gtGetOp2();
976 assert(shiftBy->isContainedIntOrIImmed());
978 unsigned int count = shiftBy->AsIntConCommon()->IconValue();
980 regNumber regResult = (oper == GT_LSH_HI) ? regHi : regLo;
982 if (regResult != tree->gtRegNum)
984 inst_RV_RV(INS_mov, tree->gtRegNum, regResult, targetType);
987 if (oper == GT_LSH_HI)
989 inst_RV_SH(ins, EA_4BYTE, tree->gtRegNum, count);
990 getEmitter()->emitIns_R_R_R_I(INS_OR, EA_4BYTE, tree->gtRegNum, tree->gtRegNum, regLo, 32 - count,
991 INS_FLAGS_DONT_CARE, INS_OPTS_LSR);
995 assert(oper == GT_RSH_LO);
996 inst_RV_SH(INS_SHIFT_RIGHT_LOGICAL, EA_4BYTE, tree->gtRegNum, count);
997 getEmitter()->emitIns_R_R_R_I(INS_OR, EA_4BYTE, tree->gtRegNum, tree->gtRegNum, regHi, 32 - count,
998 INS_FLAGS_DONT_CARE, INS_OPTS_LSL);
1001 genProduceReg(tree);
1004 //------------------------------------------------------------------------
1005 // genCodeForLclVar: Produce code for a GT_LCL_VAR node.
1008 // tree - the GT_LCL_VAR node
1010 void CodeGen::genCodeForLclVar(GenTreeLclVar* tree)
1012 // lcl_vars are not defs
1013 assert((tree->gtFlags & GTF_VAR_DEF) == 0);
1015 bool isRegCandidate = compiler->lvaTable[tree->gtLclNum].lvIsRegCandidate();
1017 if (isRegCandidate && !(tree->gtFlags & GTF_VAR_DEATH))
1019 assert((tree->InReg()) || (tree->gtFlags & GTF_SPILLED));
1022 // If this is a register candidate that has been spilled, genConsumeReg() will
1023 // reload it at the point of use. Otherwise, if it's not in a register, we load it here.
1025 if (!tree->InReg() && !(tree->gtFlags & GTF_SPILLED))
1027 assert(!isRegCandidate);
1028 getEmitter()->emitIns_R_S(ins_Load(tree->TypeGet()), emitTypeSize(tree), tree->gtRegNum, tree->gtLclNum, 0);
1029 genProduceReg(tree);
1033 //------------------------------------------------------------------------
1034 // genCodeForStoreLclFld: Produce code for a GT_STORE_LCL_FLD node.
1037 // tree - the GT_STORE_LCL_FLD node
1039 void CodeGen::genCodeForStoreLclFld(GenTreeLclFld* tree)
1041 var_types targetType = tree->TypeGet();
1042 regNumber targetReg = tree->gtRegNum;
1043 emitter* emit = getEmitter();
1045 noway_assert(targetType != TYP_STRUCT);
1047 // record the offset
1048 unsigned offset = tree->gtLclOffs;
1050 // We must have a stack store with GT_STORE_LCL_FLD
1051 noway_assert(!tree->InReg());
1052 noway_assert(targetReg == REG_NA);
1054 unsigned varNum = tree->gtLclNum;
1055 assert(varNum < compiler->lvaCount);
1056 LclVarDsc* varDsc = &(compiler->lvaTable[varNum]);
1058 // Ensure that lclVar nodes are typed correctly.
1059 assert(!varDsc->lvNormalizeOnStore() || targetType == genActualType(varDsc->TypeGet()));
1061 GenTreePtr data = tree->gtOp1->gtEffectiveVal();
1062 instruction ins = ins_Store(targetType);
1063 emitAttr attr = emitTypeSize(targetType);
1064 if (data->isContainedIntOrIImmed())
1066 assert(data->IsIntegralConst(0));
1067 NYI_ARM("st.lclFld contained operand");
1071 assert(!data->isContained());
1072 genConsumeReg(data);
1073 emit->emitIns_S_R(ins, attr, data->gtRegNum, varNum, offset);
1076 genUpdateLife(tree);
1077 varDsc->lvRegNum = REG_STK;
1080 //------------------------------------------------------------------------
1081 // genCodeForStoreLclVar: Produce code for a GT_STORE_LCL_VAR node.
1084 // tree - the GT_STORE_LCL_VAR node
1086 void CodeGen::genCodeForStoreLclVar(GenTreeLclVar* tree)
1088 var_types targetType = tree->TypeGet();
1089 regNumber targetReg = tree->gtRegNum;
1090 emitter* emit = getEmitter();
1092 unsigned varNum = tree->gtLclNum;
1093 assert(varNum < compiler->lvaCount);
1094 LclVarDsc* varDsc = &(compiler->lvaTable[varNum]);
1096 // Ensure that lclVar nodes are typed correctly.
1097 assert(!varDsc->lvNormalizeOnStore() || targetType == genActualType(varDsc->TypeGet()));
1099 GenTreePtr data = tree->gtOp1->gtEffectiveVal();
1101 // var = call, where call returns a multi-reg return value
1102 // case is handled separately.
1103 if (data->gtSkipReloadOrCopy()->IsMultiRegCall())
1105 genMultiRegCallStoreToLocal(tree);
1107 else if (tree->TypeGet() == TYP_LONG)
1109 genStoreLongLclVar(tree);
1113 genConsumeRegs(data);
1115 regNumber dataReg = REG_NA;
1116 if (data->isContainedIntOrIImmed())
1118 assert(data->IsIntegralConst(0));
1119 NYI_ARM("st.lclVar contained operand");
1123 assert(!data->isContained());
1124 dataReg = data->gtRegNum;
1126 assert(dataReg != REG_NA);
1128 if (targetReg == REG_NA) // store into stack based LclVar
1130 inst_set_SV_var(tree);
1132 instruction ins = ins_Store(targetType);
1133 emitAttr attr = emitTypeSize(targetType);
1135 emit->emitIns_S_R(ins, attr, dataReg, varNum, /* offset */ 0);
1137 genUpdateLife(tree);
1139 varDsc->lvRegNum = REG_STK;
1141 else // store into register (i.e move into register)
1143 if (dataReg != targetReg)
1145 // Assign into targetReg when dataReg (from op1) is not the same register
1146 inst_RV_RV(ins_Copy(targetType), targetReg, dataReg, targetType);
1148 genProduceReg(tree);
1153 //------------------------------------------------------------------------
1154 // genLeaInstruction: Produce code for a GT_LEA subnode.
1156 void CodeGen::genLeaInstruction(GenTreeAddrMode* lea)
1158 emitAttr size = emitTypeSize(lea);
1159 genConsumeOperands(lea);
1161 if (lea->Base() && lea->Index())
1163 regNumber baseReg = lea->Base()->gtRegNum;
1164 regNumber indexReg = lea->Index()->gtRegNum;
1165 getEmitter()->emitIns_R_ARX(INS_lea, size, lea->gtRegNum, baseReg, indexReg, lea->gtScale, lea->gtOffset);
1167 else if (lea->Base())
1169 regNumber baseReg = lea->Base()->gtRegNum;
1170 getEmitter()->emitIns_R_AR(INS_lea, size, lea->gtRegNum, baseReg, lea->gtOffset);
1172 else if (lea->Index())
1174 assert(!"Should we see a baseless address computation during CodeGen for ARM32?");
1180 //------------------------------------------------------------------------
1181 // genCodeForDivMod: Produce code for a GT_DIV/GT_UDIV/GT_MOD/GT_UMOD node.
1186 void CodeGen::genCodeForDivMod(GenTreeOp* tree)
1188 assert(tree->OperIs(GT_DIV, GT_UDIV, GT_MOD, GT_UMOD));
1190 // We shouldn't be seeing GT_MOD on float/double args as it should get morphed into a
1191 // helper call by front-end. Similarly we shouldn't be seeing GT_UDIV and GT_UMOD
1192 // on float/double args.
1193 noway_assert(tree->OperIs(GT_DIV) || !varTypeIsFloating(tree));
1195 var_types targetType = tree->TypeGet();
1196 regNumber targetReg = tree->gtRegNum;
1197 emitter* emit = getEmitter();
1199 genConsumeOperands(tree);
1201 noway_assert(targetReg != REG_NA);
1203 GenTreePtr dst = tree;
1204 GenTreePtr src1 = tree->gtGetOp1();
1205 GenTreePtr src2 = tree->gtGetOp2();
1206 instruction ins = genGetInsForOper(tree->OperGet(), targetType);
1207 emitAttr attr = emitTypeSize(tree);
1208 regNumber result = REG_NA;
1210 // dst can only be a reg
1211 assert(!dst->isContained());
1213 // src can be only reg
1214 assert(!src1->isContained() || !src2->isContained());
1216 if (varTypeIsFloating(targetType))
1218 // Floating point divide never raises an exception
1220 emit->emitIns_R_R_R(ins, attr, dst->gtRegNum, src1->gtRegNum, src2->gtRegNum);
1222 else // an signed integer divide operation
1224 // TODO-ARM-Bug: handle zero division exception.
1226 emit->emitIns_R_R_R(ins, attr, dst->gtRegNum, src1->gtRegNum, src2->gtRegNum);
1229 genProduceReg(tree);
1232 //------------------------------------------------------------------------
1233 // genCkfinite: Generate code for ckfinite opcode.
1236 // treeNode - The GT_CKFINITE node
1242 // GT_CKFINITE node has reserved an internal register.
1244 void CodeGen::genCkfinite(GenTreePtr treeNode)
1246 assert(treeNode->OperGet() == GT_CKFINITE);
1248 emitter* emit = getEmitter();
1249 var_types targetType = treeNode->TypeGet();
1250 regNumber intReg = treeNode->GetSingleTempReg();
1251 regNumber fpReg = genConsumeReg(treeNode->gtOp.gtOp1);
1252 regNumber targetReg = treeNode->gtRegNum;
1254 // Extract and sign-extend the exponent into an integer register
1255 if (targetType == TYP_FLOAT)
1257 emit->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, intReg, fpReg);
1258 emit->emitIns_R_R_I_I(INS_sbfx, EA_4BYTE, intReg, intReg, 23, 8);
1262 assert(targetType == TYP_DOUBLE);
1263 emit->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, intReg, REG_NEXT(fpReg));
1264 emit->emitIns_R_R_I_I(INS_sbfx, EA_4BYTE, intReg, intReg, 20, 11);
1267 // If exponent is all 1's, throw ArithmeticException
1268 emit->emitIns_R_I(INS_add, EA_4BYTE, intReg, 1, INS_FLAGS_SET);
1269 genJumpToThrowHlpBlk(EJ_eq, SCK_ARITH_EXCPN);
1271 // If it's a finite value, copy it to targetReg
1272 if (targetReg != fpReg)
1274 emit->emitIns_R_R(ins_Copy(targetType), emitTypeSize(treeNode), targetReg, fpReg);
1276 genProduceReg(treeNode);
1279 //------------------------------------------------------------------------
1280 // genCodeForCompare: Produce code for a GT_EQ/GT_NE/GT_LT/GT_LE/GT_GE/GT_GT node.
1285 void CodeGen::genCodeForCompare(GenTreeOp* tree)
1287 // TODO-ARM-CQ: Check if we can use the currently set flags.
1288 // TODO-ARM-CQ: Check for the case where we can simply transfer the carry bit to a register
1289 // (signed < or >= where targetReg != REG_NA)
1291 GenTreePtr op1 = tree->gtOp1->gtEffectiveVal();
1292 GenTreePtr op2 = tree->gtOp2->gtEffectiveVal();
1293 var_types op1Type = op1->TypeGet();
1294 var_types op2Type = op2->TypeGet();
1296 if (varTypeIsLong(op1Type))
1299 // The result of an unlowered long compare on a 32-bit target must either be
1300 // a) materialized into a register, or
1303 // A long compare that has a result that is used but not materialized into a register should
1304 // have been handled by Lowering::LowerCompare.
1307 assert((tree->gtRegNum != REG_NA) || !LIR::AsRange(compiler->compCurBB).TryGetUse(tree, &use));
1309 genCompareLong(tree);
1313 assert(!varTypeIsLong(op2Type));
1315 regNumber targetReg = tree->gtRegNum;
1316 emitter* emit = getEmitter();
1318 genConsumeIfReg(op1);
1319 genConsumeIfReg(op2);
1321 if (varTypeIsFloating(op1Type))
1323 assert(op1Type == op2Type);
1324 emit->emitInsBinary(INS_vcmp, emitTypeSize(op1Type), op1, op2);
1325 // vmrs with register 0xf has special meaning of transferring flags
1326 emit->emitIns_R(INS_vmrs, EA_4BYTE, REG_R15);
1330 assert(!varTypeIsFloating(op2Type));
1331 var_types cmpType = (op1Type == op2Type) ? op1Type : TYP_INT;
1332 emit->emitInsBinary(INS_cmp, emitTypeSize(cmpType), op1, op2);
1335 // Are we evaluating this into a register?
1336 if (targetReg != REG_NA)
1338 genSetRegToCond(targetReg, tree);
1339 genProduceReg(tree);
1344 //------------------------------------------------------------------------
1345 // genCodeForJcc: Produce code for a GT_JCC node.
1350 void CodeGen::genCodeForJcc(GenTreeJumpCC* tree)
1352 assert(compiler->compCurBB->bbJumpKind == BBJ_COND);
1354 CompareKind compareKind = ((tree->gtFlags & GTF_UNSIGNED) != 0) ? CK_UNSIGNED : CK_SIGNED;
1355 emitJumpKind jumpKind = genJumpKindForOper(tree->gtCondition, compareKind);
1357 inst_JMP(jumpKind, compiler->compCurBB->bbJumpDest);
1360 //------------------------------------------------------------------------
1361 // genCodeForReturnTrap: Produce code for a GT_RETURNTRAP node.
1364 // tree - the GT_RETURNTRAP node
1366 void CodeGen::genCodeForReturnTrap(GenTreeOp* tree)
1368 assert(tree->OperGet() == GT_RETURNTRAP);
1370 // this is nothing but a conditional call to CORINFO_HELP_STOP_FOR_GC
1371 // based on the contents of 'data'
1373 GenTree* data = tree->gtOp1->gtEffectiveVal();
1374 genConsumeIfReg(data);
1375 GenTreeIntCon cns = intForm(TYP_INT, 0);
1376 getEmitter()->emitInsBinary(INS_cmp, emitTypeSize(TYP_INT), data, &cns);
1378 BasicBlock* skipLabel = genCreateTempLabel();
1380 emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED);
1381 inst_JMP(jmpEqual, skipLabel);
1382 // emit the call to the EE-helper that stops for GC (or other reasons)
1384 genEmitHelperCall(CORINFO_HELP_STOP_FOR_GC, 0, EA_UNKNOWN);
1385 genDefineTempLabel(skipLabel);
1388 //------------------------------------------------------------------------
1389 // genCodeForStoreInd: Produce code for a GT_STOREIND node.
1392 // tree - the GT_STOREIND node
1394 void CodeGen::genCodeForStoreInd(GenTreeStoreInd* tree)
1396 GenTree* data = tree->Data();
1397 GenTree* addr = tree->Addr();
1398 var_types targetType = tree->TypeGet();
1399 emitter* emit = getEmitter();
1401 assert(!varTypeIsFloating(targetType) || (targetType == data->TypeGet()));
1403 GCInfo::WriteBarrierForm writeBarrierForm = gcInfo.gcIsWriteBarrierCandidate(tree, data);
1404 if (writeBarrierForm != GCInfo::WBF_NoBarrier)
1406 // data and addr must be in registers.
1407 // Consume both registers so that any copies of interfering
1408 // registers are taken care of.
1409 genConsumeOperands(tree);
1411 #if NOGC_WRITE_BARRIERS
1412 NYI_ARM("NOGC_WRITE_BARRIERS");
1414 // At this point, we should not have any interference.
1415 // That is, 'data' must not be in REG_ARG_0,
1416 // as that is where 'addr' must go.
1417 noway_assert(data->gtRegNum != REG_ARG_0);
1419 // addr goes in REG_ARG_0
1420 if (addr->gtRegNum != REG_ARG_0)
1422 inst_RV_RV(INS_mov, REG_ARG_0, addr->gtRegNum, addr->TypeGet());
1425 // data goes in REG_ARG_1
1426 if (data->gtRegNum != REG_ARG_1)
1428 inst_RV_RV(INS_mov, REG_ARG_1, data->gtRegNum, data->TypeGet());
1430 #endif // NOGC_WRITE_BARRIERS
1432 genGCWriteBarrier(tree, writeBarrierForm);
1434 else // A normal store, not a WriteBarrier store
1436 bool dataIsUnary = false;
1438 // We must consume the operands in the proper execution order,
1439 // so that liveness is updated appropriately.
1440 genConsumeAddress(addr);
1442 if (!data->isContained())
1444 genConsumeRegs(data);
1447 emit->emitInsLoadStoreOp(ins_Store(targetType), emitTypeSize(tree), data->gtRegNum, tree);
1451 //------------------------------------------------------------------------
1452 // genCompareLong: Generate code for comparing two longs when the result of the compare
1453 // is manifested in a register.
1456 // treeNode - the compare tree
1462 // For long compares, we need to compare the high parts of operands first, then the low parts.
1463 // If the high compare is false, we do not need to compare the low parts. For less than and
1464 // greater than, if the high compare is true, we can assume the entire compare is true.
1466 void CodeGen::genCompareLong(GenTreePtr treeNode)
1468 assert(treeNode->OperIsCompare());
1470 GenTreeOp* tree = treeNode->AsOp();
1471 GenTreePtr op1 = tree->gtOp1;
1472 GenTreePtr op2 = tree->gtOp2;
1474 assert(varTypeIsLong(op1->TypeGet()));
1475 assert(varTypeIsLong(op2->TypeGet()));
1477 regNumber targetReg = treeNode->gtRegNum;
1479 genConsumeOperands(tree);
1481 GenTreePtr loOp1 = op1->gtGetOp1();
1482 GenTreePtr hiOp1 = op1->gtGetOp2();
1483 GenTreePtr loOp2 = op2->gtGetOp1();
1484 GenTreePtr hiOp2 = op2->gtGetOp2();
1486 // Create compare for the high parts
1487 instruction ins = INS_cmp;
1488 var_types cmpType = TYP_INT;
1489 emitAttr cmpAttr = emitTypeSize(cmpType);
1491 // Emit the compare instruction
1492 getEmitter()->emitInsBinary(ins, cmpAttr, hiOp1, hiOp2);
1494 // If the result is not being materialized in a register, we're done.
1495 if (targetReg == REG_NA)
1500 BasicBlock* labelTrue = genCreateTempLabel();
1501 BasicBlock* labelFalse = genCreateTempLabel();
1502 BasicBlock* labelNext = genCreateTempLabel();
1504 genJccLongHi(tree->gtOper, labelTrue, labelFalse, tree->IsUnsigned());
1505 getEmitter()->emitInsBinary(ins, cmpAttr, loOp1, loOp2);
1506 genJccLongLo(tree->gtOper, labelTrue, labelFalse);
1508 genDefineTempLabel(labelFalse);
1509 getEmitter()->emitIns_R_I(INS_mov, emitActualTypeSize(tree->gtType), tree->gtRegNum, 0);
1510 getEmitter()->emitIns_J(INS_b, labelNext);
1512 genDefineTempLabel(labelTrue);
1513 getEmitter()->emitIns_R_I(INS_mov, emitActualTypeSize(tree->gtType), tree->gtRegNum, 1);
1515 genDefineTempLabel(labelNext);
1517 genProduceReg(tree);
1520 void CodeGen::genJccLongHi(genTreeOps cmp, BasicBlock* jumpTrue, BasicBlock* jumpFalse, bool isUnsigned)
1524 jumpFalse->bbFlags |= BBF_JMP_TARGET | BBF_HAS_LABEL;
1530 inst_JMP(EJ_ne, jumpFalse);
1534 inst_JMP(EJ_ne, jumpTrue);
1541 inst_JMP(EJ_hi, jumpFalse);
1542 inst_JMP(EJ_lo, jumpTrue);
1546 inst_JMP(EJ_gt, jumpFalse);
1547 inst_JMP(EJ_lt, jumpTrue);
1555 inst_JMP(EJ_lo, jumpFalse);
1556 inst_JMP(EJ_hi, jumpTrue);
1560 inst_JMP(EJ_lt, jumpFalse);
1561 inst_JMP(EJ_gt, jumpTrue);
1566 noway_assert(!"expected a comparison operator");
1570 void CodeGen::genJccLongLo(genTreeOps cmp, BasicBlock* jumpTrue, BasicBlock* jumpFalse)
1575 inst_JMP(EJ_eq, jumpTrue);
1579 inst_JMP(EJ_ne, jumpTrue);
1583 inst_JMP(EJ_lo, jumpTrue);
1587 inst_JMP(EJ_ls, jumpTrue);
1591 inst_JMP(EJ_hs, jumpTrue);
1595 inst_JMP(EJ_hi, jumpTrue);
1599 noway_assert(!"expected comparison");
1603 //------------------------------------------------------------------------
1604 // genSetRegToCond: Generate code to materialize a condition into a register.
1607 // dstReg - The target register to set to 1 or 0
1608 // tree - The GenTree Relop node that was used to set the Condition codes
1610 // Return Value: none
1613 // The condition codes must already have been appropriately set.
1615 void CodeGen::genSetRegToCond(regNumber dstReg, GenTreePtr tree)
1617 // Emit code like that:
1627 CompareKind compareKind = ((tree->gtFlags & GTF_UNSIGNED) != 0) ? CK_UNSIGNED : CK_SIGNED;
1628 emitJumpKind jmpKind = genJumpKindForOper(tree->gtOper, compareKind);
1630 BasicBlock* labelTrue = genCreateTempLabel();
1631 getEmitter()->emitIns_J(emitter::emitJumpKindToIns(jmpKind), labelTrue);
1633 getEmitter()->emitIns_R_I(INS_mov, emitActualTypeSize(tree->gtType), dstReg, 0);
1635 BasicBlock* labelNext = genCreateTempLabel();
1636 getEmitter()->emitIns_J(INS_b, labelNext);
1638 genDefineTempLabel(labelTrue);
1639 getEmitter()->emitIns_R_I(INS_mov, emitActualTypeSize(tree->gtType), dstReg, 1);
1640 genDefineTempLabel(labelNext);
1643 //------------------------------------------------------------------------
1644 // genLongToIntCast: Generate code for long to int casts.
1647 // cast - The GT_CAST node
1653 // The cast node and its sources (via GT_LONG) must have been assigned registers.
1654 // The destination cannot be a floating point type or a small integer type.
1656 void CodeGen::genLongToIntCast(GenTree* cast)
1658 assert(cast->OperGet() == GT_CAST);
1660 GenTree* src = cast->gtGetOp1();
1661 noway_assert(src->OperGet() == GT_LONG);
1663 genConsumeRegs(src);
1665 var_types srcType = ((cast->gtFlags & GTF_UNSIGNED) != 0) ? TYP_ULONG : TYP_LONG;
1666 var_types dstType = cast->CastToType();
1667 regNumber loSrcReg = src->gtGetOp1()->gtRegNum;
1668 regNumber hiSrcReg = src->gtGetOp2()->gtRegNum;
1669 regNumber dstReg = cast->gtRegNum;
1671 assert((dstType == TYP_INT) || (dstType == TYP_UINT));
1672 assert(genIsValidIntReg(loSrcReg));
1673 assert(genIsValidIntReg(hiSrcReg));
1674 assert(genIsValidIntReg(dstReg));
1676 if (cast->gtOverflow())
1679 // Generate an overflow check for [u]long to [u]int casts:
1681 // long -> int - check if the upper 33 bits are all 0 or all 1
1683 // ulong -> int - check if the upper 33 bits are all 0
1685 // long -> uint - check if the upper 32 bits are all 0
1686 // ulong -> uint - check if the upper 32 bits are all 0
1689 if ((srcType == TYP_LONG) && (dstType == TYP_INT))
1691 BasicBlock* allOne = genCreateTempLabel();
1692 BasicBlock* success = genCreateTempLabel();
1694 inst_RV_RV(INS_tst, loSrcReg, loSrcReg, TYP_INT, EA_4BYTE);
1695 emitJumpKind JmpNegative = genJumpKindForOper(GT_LT, CK_LOGICAL);
1696 inst_JMP(JmpNegative, allOne);
1697 inst_RV_RV(INS_tst, hiSrcReg, hiSrcReg, TYP_INT, EA_4BYTE);
1698 emitJumpKind jmpNotEqualL = genJumpKindForOper(GT_NE, CK_LOGICAL);
1699 genJumpToThrowHlpBlk(jmpNotEqualL, SCK_OVERFLOW);
1700 inst_JMP(EJ_jmp, success);
1702 genDefineTempLabel(allOne);
1703 inst_RV_IV(INS_cmp, hiSrcReg, -1, EA_4BYTE);
1704 emitJumpKind jmpNotEqualS = genJumpKindForOper(GT_NE, CK_SIGNED);
1705 genJumpToThrowHlpBlk(jmpNotEqualS, SCK_OVERFLOW);
1707 genDefineTempLabel(success);
1711 if ((srcType == TYP_ULONG) && (dstType == TYP_INT))
1713 inst_RV_RV(INS_tst, loSrcReg, loSrcReg, TYP_INT, EA_4BYTE);
1714 emitJumpKind JmpNegative = genJumpKindForOper(GT_LT, CK_LOGICAL);
1715 genJumpToThrowHlpBlk(JmpNegative, SCK_OVERFLOW);
1718 inst_RV_RV(INS_tst, hiSrcReg, hiSrcReg, TYP_INT, EA_4BYTE);
1719 emitJumpKind jmpNotEqual = genJumpKindForOper(GT_NE, CK_LOGICAL);
1720 genJumpToThrowHlpBlk(jmpNotEqual, SCK_OVERFLOW);
1724 if (dstReg != loSrcReg)
1726 inst_RV_RV(INS_mov, dstReg, loSrcReg, TYP_INT, EA_4BYTE);
1729 genProduceReg(cast);
1732 //------------------------------------------------------------------------
1733 // genIntToFloatCast: Generate code to cast an int/long to float/double
1736 // treeNode - The GT_CAST node
1742 // Cast is a non-overflow conversion.
1743 // The treeNode must have an assigned register.
1744 // SrcType= int32/uint32/int64/uint64 and DstType=float/double.
1746 void CodeGen::genIntToFloatCast(GenTreePtr treeNode)
1748 // int --> float/double conversions are always non-overflow ones
1749 assert(treeNode->OperGet() == GT_CAST);
1750 assert(!treeNode->gtOverflow());
1752 regNumber targetReg = treeNode->gtRegNum;
1753 assert(genIsValidFloatReg(targetReg));
1755 GenTreePtr op1 = treeNode->gtOp.gtOp1;
1756 assert(!op1->isContained()); // Cannot be contained
1757 assert(genIsValidIntReg(op1->gtRegNum)); // Must be a valid int reg.
1759 var_types dstType = treeNode->CastToType();
1760 var_types srcType = op1->TypeGet();
1761 assert(!varTypeIsFloating(srcType) && varTypeIsFloating(dstType));
1763 // force the srcType to unsigned if GT_UNSIGNED flag is set
1764 if (treeNode->gtFlags & GTF_UNSIGNED)
1766 srcType = genUnsignedType(srcType);
1769 // We should never see a srcType whose size is neither EA_4BYTE or EA_8BYTE
1770 // For conversions from small types (byte/sbyte/int16/uint16) to float/double,
1771 // we expect the front-end or lowering phase to have generated two levels of cast.
1773 emitAttr srcSize = EA_ATTR(genTypeSize(srcType));
1774 noway_assert((srcSize == EA_4BYTE) || (srcSize == EA_8BYTE));
1776 instruction insVcvt = INS_invalid;
1778 if (dstType == TYP_DOUBLE)
1780 if (srcSize == EA_4BYTE)
1782 insVcvt = (varTypeIsUnsigned(srcType)) ? INS_vcvt_u2d : INS_vcvt_i2d;
1786 assert(srcSize == EA_8BYTE);
1787 NYI_ARM("Casting int64/uint64 to double in genIntToFloatCast");
1792 assert(dstType == TYP_FLOAT);
1793 if (srcSize == EA_4BYTE)
1795 insVcvt = (varTypeIsUnsigned(srcType)) ? INS_vcvt_u2f : INS_vcvt_i2f;
1799 assert(srcSize == EA_8BYTE);
1800 NYI_ARM("Casting int64/uint64 to float in genIntToFloatCast");
1804 genConsumeOperands(treeNode->AsOp());
1806 assert(insVcvt != INS_invalid);
1807 getEmitter()->emitIns_R_R(INS_vmov_i2f, srcSize, treeNode->gtRegNum, op1->gtRegNum);
1808 getEmitter()->emitIns_R_R(insVcvt, srcSize, treeNode->gtRegNum, treeNode->gtRegNum);
1810 genProduceReg(treeNode);
1813 //------------------------------------------------------------------------
1814 // genFloatToIntCast: Generate code to cast float/double to int/long
1817 // treeNode - The GT_CAST node
1823 // Cast is a non-overflow conversion.
1824 // The treeNode must have an assigned register.
1825 // SrcType=float/double and DstType= int32/uint32/int64/uint64
1827 void CodeGen::genFloatToIntCast(GenTreePtr treeNode)
1829 // we don't expect to see overflow detecting float/double --> int type conversions here
1830 // as they should have been converted into helper calls by front-end.
1831 assert(treeNode->OperGet() == GT_CAST);
1832 assert(!treeNode->gtOverflow());
1834 regNumber targetReg = treeNode->gtRegNum;
1835 assert(genIsValidIntReg(targetReg)); // Must be a valid int reg.
1837 GenTreePtr op1 = treeNode->gtOp.gtOp1;
1838 assert(!op1->isContained()); // Cannot be contained
1839 assert(genIsValidFloatReg(op1->gtRegNum)); // Must be a valid float reg.
1841 var_types dstType = treeNode->CastToType();
1842 var_types srcType = op1->TypeGet();
1843 assert(varTypeIsFloating(srcType) && !varTypeIsFloating(dstType));
1845 // We should never see a dstType whose size is neither EA_4BYTE or EA_8BYTE
1846 // For conversions to small types (byte/sbyte/int16/uint16) from float/double,
1847 // we expect the front-end or lowering phase to have generated two levels of cast.
1849 emitAttr dstSize = EA_ATTR(genTypeSize(dstType));
1850 noway_assert((dstSize == EA_4BYTE) || (dstSize == EA_8BYTE));
1852 instruction insVcvt = INS_invalid;
1854 if (srcType == TYP_DOUBLE)
1856 if (dstSize == EA_4BYTE)
1858 insVcvt = (varTypeIsUnsigned(dstType)) ? INS_vcvt_d2u : INS_vcvt_d2i;
1862 assert(dstSize == EA_8BYTE);
1863 NYI_ARM("Casting double to int64/uint64 in genIntToFloatCast");
1868 assert(srcType == TYP_FLOAT);
1869 if (dstSize == EA_4BYTE)
1871 insVcvt = (varTypeIsUnsigned(dstType)) ? INS_vcvt_f2u : INS_vcvt_f2i;
1875 assert(dstSize == EA_8BYTE);
1876 NYI_ARM("Casting float to int64/uint64 in genIntToFloatCast");
1880 genConsumeOperands(treeNode->AsOp());
1882 assert(insVcvt != INS_invalid);
1883 getEmitter()->emitIns_R_R(insVcvt, dstSize, op1->gtRegNum, op1->gtRegNum);
1884 getEmitter()->emitIns_R_R(INS_vmov_f2i, dstSize, treeNode->gtRegNum, op1->gtRegNum);
1886 genProduceReg(treeNode);
1889 //------------------------------------------------------------------------
1890 // genEmitHelperCall: Emit a call to a helper function.
1892 void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, regNumber callTargetReg /*= REG_NA */)
1894 // Can we call the helper function directly
1896 void *addr = NULL, **pAddr = NULL;
1898 #if defined(DEBUG) && defined(PROFILING_SUPPORTED)
1899 // Don't ask VM if it hasn't requested ELT hooks
1900 if (!compiler->compProfilerHookNeeded && compiler->opts.compJitELTHookEnabled &&
1901 (helper == CORINFO_HELP_PROF_FCN_ENTER || helper == CORINFO_HELP_PROF_FCN_LEAVE ||
1902 helper == CORINFO_HELP_PROF_FCN_TAILCALL))
1904 addr = compiler->compProfilerMethHnd;
1909 addr = compiler->compGetHelperFtn((CorInfoHelpFunc)helper, (void**)&pAddr);
1912 if (!addr || !arm_Valid_Imm_For_BL((ssize_t)addr))
1914 if (callTargetReg == REG_NA)
1916 // If a callTargetReg has not been explicitly provided, we will use REG_DEFAULT_HELPER_CALL_TARGET, but
1917 // this is only a valid assumption if the helper call is known to kill REG_DEFAULT_HELPER_CALL_TARGET.
1918 callTargetReg = REG_DEFAULT_HELPER_CALL_TARGET;
1921 // Load the address into a register and call through a register
1924 instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, callTargetReg, (ssize_t)addr);
1928 getEmitter()->emitIns_R_AI(INS_ldr, EA_PTR_DSP_RELOC, callTargetReg, (ssize_t)pAddr);
1929 regTracker.rsTrackRegTrash(callTargetReg);
1932 getEmitter()->emitIns_Call(emitter::EC_INDIR_R, compiler->eeFindHelper(helper),
1933 INDEBUG_LDISASM_COMMA(nullptr) NULL, // addr
1934 argSize, retSize, gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur,
1935 gcInfo.gcRegByrefSetCur,
1936 BAD_IL_OFFSET, // ilOffset
1937 callTargetReg, // ireg
1938 REG_NA, 0, 0, // xreg, xmul, disp
1940 emitter::emitNoGChelper(helper),
1941 (CorInfoHelpFunc)helper == CORINFO_HELP_PROF_FCN_LEAVE);
1945 getEmitter()->emitIns_Call(emitter::EC_FUNC_TOKEN, compiler->eeFindHelper(helper),
1946 INDEBUG_LDISASM_COMMA(nullptr) addr, argSize, retSize, gcInfo.gcVarPtrSetCur,
1947 gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, BAD_IL_OFFSET, REG_NA, REG_NA, 0,
1948 0, /* ilOffset, ireg, xreg, xmul, disp */
1950 emitter::emitNoGChelper(helper),
1951 (CorInfoHelpFunc)helper == CORINFO_HELP_PROF_FCN_LEAVE);
1954 regTracker.rsTrashRegSet(RBM_CALLEE_TRASH);
1955 regTracker.rsTrashRegsForGCInterruptability();
1958 //------------------------------------------------------------------------
1959 // genStoreLongLclVar: Generate code to store a non-enregistered long lclVar
1962 // treeNode - A TYP_LONG lclVar node.
1968 // 'treeNode' must be a TYP_LONG lclVar node for a lclVar that has NOT been promoted.
1969 // Its operand must be a GT_LONG node.
1971 void CodeGen::genStoreLongLclVar(GenTree* treeNode)
1973 emitter* emit = getEmitter();
1975 GenTreeLclVarCommon* lclNode = treeNode->AsLclVarCommon();
1976 unsigned lclNum = lclNode->gtLclNum;
1977 LclVarDsc* varDsc = &(compiler->lvaTable[lclNum]);
1978 assert(varDsc->TypeGet() == TYP_LONG);
1979 assert(!varDsc->lvPromoted);
1980 GenTreePtr op1 = treeNode->gtOp.gtOp1;
1981 noway_assert(op1->OperGet() == GT_LONG || op1->OperGet() == GT_MUL_LONG);
1982 genConsumeRegs(op1);
1984 if (op1->OperGet() == GT_LONG)
1986 // Definitions of register candidates will have been lowered to 2 int lclVars.
1987 assert(!treeNode->InReg());
1989 GenTreePtr loVal = op1->gtGetOp1();
1990 GenTreePtr hiVal = op1->gtGetOp2();
1992 // NYI: Contained immediates.
1993 NYI_IF((loVal->gtRegNum == REG_NA) || (hiVal->gtRegNum == REG_NA),
1994 "Store of long lclVar with contained immediate");
1996 emit->emitIns_S_R(ins_Store(TYP_INT), EA_4BYTE, loVal->gtRegNum, lclNum, 0);
1997 emit->emitIns_S_R(ins_Store(TYP_INT), EA_4BYTE, hiVal->gtRegNum, lclNum, genTypeSize(TYP_INT));
1999 else if (op1->OperGet() == GT_MUL_LONG)
2001 assert((op1->gtFlags & GTF_MUL_64RSLT) != 0);
2004 getEmitter()->emitIns_S_R(ins_Store(TYP_INT), emitTypeSize(TYP_INT), REG_LNGRET_LO, lclNum, 0);
2005 getEmitter()->emitIns_S_R(ins_Store(TYP_INT), emitTypeSize(TYP_INT), REG_LNGRET_HI, lclNum,
2006 genTypeSize(TYP_INT));
2010 #endif // _TARGET_ARM_
2012 #endif // !LEGACY_BACKEND