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();
263 if (targetType == TYP_VOID)
265 assert(op1 == nullptr);
269 if (treeNode->TypeGet() == TYP_LONG)
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));
277 genConsumeReg(loRetVal);
278 genConsumeReg(hiRetVal);
279 if (loRetVal->gtRegNum != REG_LNGRET_LO)
281 inst_RV_RV(ins_Copy(targetType), REG_LNGRET_LO, loRetVal->gtRegNum, TYP_INT);
283 if (hiRetVal->gtRegNum != REG_LNGRET_HI)
285 inst_RV_RV(ins_Copy(targetType), REG_LNGRET_HI, hiRetVal->gtRegNum, TYP_INT);
290 if (varTypeIsStruct(treeNode))
292 NYI_ARM("struct return");
294 else if (targetType != TYP_VOID)
296 assert(op1 != nullptr);
297 noway_assert(op1->gtRegNum != REG_NA);
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.
308 regNumber retReg = varTypeIsFloating(treeNode) ? REG_FLOATRET : REG_INTRET;
309 if (op1->gtRegNum != retReg)
311 inst_RV_RV(ins_Move_Extend(targetType, true), retReg, op1->gtRegNum, targetType);
317 //------------------------------------------------------------------------
318 // genCodeForTreeNode Generate code for a single node in the tree.
321 // All operands have been evaluated.
323 void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
325 regNumber targetReg = treeNode->gtRegNum;
326 var_types targetType = treeNode->TypeGet();
327 emitter* emit = getEmitter();
330 lastConsumedNode = nullptr;
331 if (compiler->verbose)
333 unsigned seqNum = treeNode->gtSeqNum; // Useful for setting a conditional break in Visual Studio
334 compiler->gtDispLIRNode(treeNode, "Generating: ");
338 // contained nodes are part of their parents for codegen purposes
339 // ex : immediates, most LEAs
340 if (treeNode->isContained())
345 switch (treeNode->gtOper)
348 genLclHeap(treeNode);
353 genSetRegToConst(targetReg, targetType, treeNode);
354 genProduceReg(treeNode);
358 assert(!varTypeIsFloating(targetType));
364 instruction ins = genGetInsForOper(treeNode->OperGet(), targetType);
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);
371 GenTreePtr operand = treeNode->gtGetOp1();
372 assert(!operand->isContained());
373 // The src must be a register.
374 regNumber operandReg = genConsumeReg(operand);
378 getEmitter()->emitIns_R_R(ins, emitTypeSize(treeNode), targetReg, operandReg);
382 getEmitter()->emitIns_R_R_I(ins, emitTypeSize(treeNode), targetReg, operandReg, 0);
385 genProduceReg(treeNode);
391 assert(varTypeIsIntegralOrI(treeNode));
401 genConsumeOperands(treeNode->AsOp());
402 genCodeForBinary(treeNode);
409 genCodeForShift(treeNode);
414 genCodeForShiftLong(treeNode);
418 // Cast is never contained (?)
419 noway_assert(targetReg != REG_NA);
421 if (varTypeIsFloating(targetType) && varTypeIsFloating(treeNode->gtOp.gtOp1))
423 // Casts float/double <--> double/float
424 genFloatToFloatCast(treeNode);
426 else if (varTypeIsFloating(treeNode->gtOp.gtOp1))
428 // Casts float/double --> int32/int64
429 genFloatToIntCast(treeNode);
431 else if (varTypeIsFloating(targetType))
433 // Casts int32/uint32/int64/uint64 --> float/double
434 genIntToFloatCast(treeNode);
438 // Casts int <--> int
439 genIntToIntCast(treeNode);
441 // The per-case functions call genProduceReg()
446 GenTreeLclVarCommon* lcl = treeNode->AsLclVarCommon();
447 // lcl_vars are not defs
448 assert((treeNode->gtFlags & GTF_VAR_DEF) == 0);
450 bool isRegCandidate = compiler->lvaTable[lcl->gtLclNum].lvIsRegCandidate();
452 if (isRegCandidate && !(treeNode->gtFlags & GTF_VAR_DEATH))
454 assert((treeNode->InReg()) || (treeNode->gtFlags & GTF_SPILLED));
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.
460 if (!treeNode->InReg() && !(treeNode->gtFlags & GTF_SPILLED))
462 assert(!isRegCandidate);
463 emit->emitIns_R_S(ins_Load(treeNode->TypeGet()), emitTypeSize(treeNode), treeNode->gtRegNum,
465 genProduceReg(treeNode);
470 case GT_LCL_FLD_ADDR:
471 case GT_LCL_VAR_ADDR:
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());
479 inst_RV_TT(INS_lea, targetReg, treeNode, 0, EA_BYREF);
481 genProduceReg(treeNode);
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");
489 emitAttr size = emitTypeSize(targetType);
490 unsigned offs = treeNode->gtLclFld.gtLclOffs;
491 unsigned varNum = treeNode->gtLclVarCommon.gtLclNum;
492 assert(varNum < compiler->lvaCount);
494 if (varTypeIsFloating(targetType))
496 if (treeNode->InReg())
498 NYI("GT_LCL_FLD with reg-to-reg floating point move");
502 emit->emitIns_R_S(ins_Load(targetType), size, targetReg, varNum, offs);
507 emit->emitIns_R_S(ins_Move_Extend(targetType, treeNode->InReg()), size, targetReg, varNum, offs);
510 genProduceReg(treeNode);
513 case GT_STORE_LCL_FLD:
515 noway_assert(targetType != TYP_STRUCT);
518 unsigned offset = treeNode->gtLclFld.gtLclOffs;
520 // We must have a stack store with GT_STORE_LCL_FLD
521 noway_assert(!treeNode->InReg());
522 noway_assert(targetReg == REG_NA);
524 GenTreeLclVarCommon* varNode = treeNode->AsLclVarCommon();
525 unsigned varNum = varNode->gtLclNum;
526 assert(varNum < compiler->lvaCount);
527 LclVarDsc* varDsc = &(compiler->lvaTable[varNum]);
529 // Ensure that lclVar nodes are typed correctly.
530 assert(!varDsc->lvNormalizeOnStore() || targetType == genActualType(varDsc->TypeGet()));
532 GenTreePtr data = treeNode->gtOp.gtOp1->gtEffectiveVal();
533 instruction ins = ins_Store(targetType);
534 emitAttr attr = emitTypeSize(targetType);
535 if (data->isContainedIntOrIImmed())
537 assert(data->IsIntegralConst(0));
538 NYI_ARM("st.lclFld contained operand");
542 assert(!data->isContained());
544 emit->emitIns_S_R(ins, attr, data->gtRegNum, varNum, offset);
547 genUpdateLife(varNode);
548 varDsc->lvRegNum = REG_STK;
552 case GT_STORE_LCL_VAR:
554 GenTreeLclVarCommon* varNode = treeNode->AsLclVarCommon();
556 unsigned varNum = varNode->gtLclNum;
557 assert(varNum < compiler->lvaCount);
558 LclVarDsc* varDsc = &(compiler->lvaTable[varNum]);
561 // Ensure that lclVar nodes are typed correctly.
562 assert(!varDsc->lvNormalizeOnStore() || targetType == genActualType(varDsc->TypeGet()));
564 GenTreePtr data = treeNode->gtOp.gtOp1->gtEffectiveVal();
566 // var = call, where call returns a multi-reg return value
567 // case is handled separately.
568 if (data->gtSkipReloadOrCopy()->IsMultiRegCall())
570 genMultiRegCallStoreToLocal(treeNode);
575 if (treeNode->TypeGet() == TYP_LONG)
577 genStoreLongLclVar(treeNode);
581 genConsumeRegs(data);
583 regNumber dataReg = REG_NA;
584 if (data->isContainedIntOrIImmed())
586 assert(data->IsIntegralConst(0));
587 NYI_ARM("st.lclVar contained operand");
591 assert(!data->isContained());
592 dataReg = data->gtRegNum;
594 assert(dataReg != REG_NA);
596 if (targetReg == REG_NA) // store into stack based LclVar
598 inst_set_SV_var(varNode);
600 instruction ins = ins_Store(targetType);
601 emitAttr attr = emitTypeSize(targetType);
603 emit->emitIns_S_R(ins, attr, dataReg, varNum, offset);
605 genUpdateLife(varNode);
607 varDsc->lvRegNum = REG_STK;
609 else // store into register (i.e move into register)
611 if (dataReg != targetReg)
613 // Assign into targetReg when dataReg (from op1) is not the same register
614 inst_RV_RV(ins_Copy(targetType), targetReg, dataReg, targetType);
616 genProduceReg(treeNode);
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)
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);
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);
645 // genLeaInstruction calls genProduceReg()
649 genConsumeAddress(treeNode->AsIndir()->Addr());
650 emit->emitInsLoadStoreOp(ins_Load(targetType), emitTypeSize(treeNode), targetReg, treeNode->AsIndir());
651 genProduceReg(treeNode);
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));
665 genConsumeOperands(treeNode->AsOp());
667 noway_assert(targetReg != REG_NA);
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;
676 // dst can only be a reg
677 assert(!dst->isContained());
679 // src can be only reg
680 assert(!src1->isContained() || !src2->isContained());
682 if (varTypeIsFloating(targetType))
684 // Floating point divide never raises an exception
686 emit->emitIns_R_R_R(ins, attr, dst->gtRegNum, src1->gtRegNum, src2->gtRegNum);
688 else // an signed integer divide operation
690 // TODO-ARM-Bug: handle zero division exception.
692 emit->emitIns_R_R_R(ins, attr, dst->gtRegNum, src1->gtRegNum, src2->gtRegNum);
695 genProduceReg(treeNode);
701 genIntrinsic(treeNode);
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)
716 GenTreeOp* tree = treeNode->AsOp();
717 GenTreePtr op1 = tree->gtOp1->gtEffectiveVal();
718 GenTreePtr op2 = tree->gtOp2->gtEffectiveVal();
720 genConsumeIfReg(op1);
721 genConsumeIfReg(op2);
723 instruction ins = INS_cmp;
725 if (varTypeIsFloating(op1))
727 assert(op1->TypeGet() == op2->TypeGet());
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);
734 else if (varTypeIsLong(op1))
737 // The result of an unlowered long compare on a 32-bit target must either be
738 // a) materialized into a register, or
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.
745 assert((treeNode->gtRegNum != REG_NA) || !LIR::AsRange(compiler->compCurBB).TryGetUse(treeNode, &use));
747 genCompareLong(treeNode);
752 var_types op1Type = op1->TypeGet();
753 var_types op2Type = op2->TypeGet();
754 assert(!varTypeIsFloating(op2Type));
756 if (op1Type == op2Type)
758 cmpAttr = emitTypeSize(op1Type);
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);
770 emit->emitInsBinary(ins, cmpAttr, op1, op2);
773 // Are we evaluating this into a register?
774 if (targetReg != REG_NA)
776 genSetRegToCond(targetReg, tree);
783 genCodeForJumpTrue(treeNode);
788 GenTreeJumpCC* jcc = treeNode->AsJumpCC();
790 assert(compiler->compCurBB->bbJumpKind == BBJ_COND);
792 CompareKind compareKind = ((jcc->gtFlags & GTF_UNSIGNED) != 0) ? CK_UNSIGNED : CK_SIGNED;
793 emitJumpKind jumpKind = genJumpKindForOper(jcc->gtCondition, compareKind);
795 inst_JMP(jumpKind, compiler->compCurBB->bbJumpDest);
801 // this is nothing but a conditional call to CORINFO_HELP_STOP_FOR_GC
802 // based on the contents of 'data'
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);
809 BasicBlock* skipLabel = genCreateTempLabel();
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)
815 genEmitHelperCall(CORINFO_HELP_STOP_FOR_GC, 0, EA_UNKNOWN);
816 genDefineTempLabel(skipLabel);
822 GenTreeStoreInd* storeInd = treeNode->AsStoreInd();
823 GenTree* data = storeInd->Data();
824 GenTree* addr = storeInd->Addr();
825 var_types targetType = storeInd->TypeGet();
827 assert(!varTypeIsFloating(targetType) || (targetType == data->TypeGet()));
829 GCInfo::WriteBarrierForm writeBarrierForm = gcInfo.gcIsWriteBarrierCandidate(treeNode, data);
830 if (writeBarrierForm != GCInfo::WBF_NoBarrier)
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());
837 #if NOGC_WRITE_BARRIERS
838 NYI_ARM("NOGC_WRITE_BARRIERS");
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);
845 // addr goes in REG_ARG_0
846 if (addr->gtRegNum != REG_ARG_0)
848 inst_RV_RV(INS_mov, REG_ARG_0, addr->gtRegNum, addr->TypeGet());
851 // data goes in REG_ARG_1
852 if (data->gtRegNum != REG_ARG_1)
854 inst_RV_RV(INS_mov, REG_ARG_1, data->gtRegNum, data->TypeGet());
856 #endif // NOGC_WRITE_BARRIERS
858 genGCWriteBarrier(storeInd, writeBarrierForm);
860 else // A normal store, not a WriteBarrier store
862 bool reverseOps = ((storeInd->gtFlags & GTF_REVERSE_OPS) != 0);
863 bool dataIsUnary = false;
865 // We must consume the operands in the proper execution order,
866 // so that liveness is updated appropriately.
869 genConsumeAddress(addr);
872 if (!data->isContained())
874 genConsumeRegs(data);
879 genConsumeAddress(addr);
882 emit->emitInsLoadStoreOp(ins_Store(targetType), emitTypeSize(storeInd), data->gtRegNum,
883 treeNode->AsIndir());
889 // This is handled at the time we call genConsumeReg() on the GT_COPY
899 genPutArgStk(treeNode->AsPutArgStk());
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
907 GenTree* op1 = treeNode->gtOp.gtOp1;
908 // If child node is not already in the register we need, move it
910 if (targetReg != op1->gtRegNum)
912 inst_RV_RV(ins_Copy(targetType), targetReg, op1->gtRegNum, targetType);
915 genProduceReg(treeNode);
919 genCallInstruction(treeNode->AsCall());
925 genLockedInstructions(treeNode->AsOp());
928 case GT_MEMORYBARRIER:
929 instGen_MemoryBarrier();
936 genProduceReg(treeNode);
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.
949 if (treeNode->gtFlags & GTF_NO_OP_NO)
951 noway_assert(!"GTF_NO_OP_NO should not be set");
959 case GT_ARR_BOUNDS_CHECK:
960 genRangeCheck(treeNode);
964 if (treeNode->gtRegNum != treeNode->AsPhysReg()->gtSrcReg)
966 inst_RV_RV(INS_mov, treeNode->gtRegNum, treeNode->AsPhysReg()->gtSrcReg, targetType);
968 genTransferRegGCState(treeNode->gtRegNum, treeNode->AsPhysReg()->gtSrcReg);
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);
985 noway_assert(handlerGetsXcptnObj(compiler->compCurBB->bbCatchTyp));
987 /* Catch arguments get passed in a register. genCodeForBBlist()
988 would have marked it as holding a GC object, but not used. */
990 noway_assert(gcInfo.gcRegGCrefSetCur & RBM_EXCEPTION_OBJECT);
991 genConsumeReg(treeNode);
994 case GT_PINVOKE_PROLOG:
995 noway_assert(((gcInfo.gcRegGCrefSetCur | gcInfo.gcRegByrefSetCur) & ~fullIntArgRegMask()) == 0);
997 // the runtime side requires the codegen here to be consistent
998 emit->emitDisableRandomNops();
1002 genPendingCallLabel = genCreateTempLabel();
1003 treeNode->gtLabel.gtLabBB = genPendingCallLabel;
1004 emit->emitIns_J_R(INS_adr, EA_PTRSIZE, genPendingCallLabel, treeNode->gtRegNum);
1007 case GT_CLS_VAR_ADDR:
1008 emit->emitIns_R_C(INS_lea, EA_PTRSIZE, targetReg, treeNode->gtClsVar.gtClsVarHnd, 0);
1009 genProduceReg(treeNode);
1012 case GT_STORE_DYN_BLK:
1014 genCodeForStoreBlk(treeNode->AsBlk());
1018 genJumpTable(treeNode);
1021 case GT_SWITCH_TABLE:
1022 genTableBasedSwitch(treeNode);
1026 genCodeForArrIndex(treeNode->AsArrIndex());
1030 genCodeForArrOffset(treeNode->AsArrOffs());
1034 // Do nothing; these nodes are simply markers for debug info.
1041 _snprintf_s(message, _countof(message), _TRUNCATE, "NYI: Unimplemented node type %s",
1042 GenTree::NodeName(treeNode->OperGet()));
1045 NYI("unimplemented node");
1052 //------------------------------------------------------------------------
1053 // genLockedInstructions: Generate code for the locked operations.
1056 // Handles GT_LOCKADD, GT_XCHG, GT_XADD nodes.
1058 void CodeGen::genLockedInstructions(GenTreeOp* treeNode)
1060 NYI("genLockedInstructions");
1063 //--------------------------------------------------------------------------------------
1064 // genLclHeap: Generate code for localloc
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
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.
1081 // Notes: Size N should be aligned to STACK_ALIGN before any allocation
1083 void CodeGen::genLclHeap(GenTreePtr tree)
1085 assert(tree->OperGet() == GT_LCLHEAP);
1087 GenTreePtr size = tree->gtOp.gtOp1;
1088 noway_assert((genActualType(size->gtType) == TYP_INT) || (genActualType(size->gtType) == TYP_I_IMPL));
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;
1103 if (compiler->opts.compStackCheckOnRet)
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);
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);
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
1121 // Whether method has PSPSym.
1123 #if FEATURE_EH_FUNCLETS
1124 hasPspSym = (compiler->lvaPSPSym != BAD_VAR_NUM);
1129 // Check to 0 size allocations
1130 // size_t amount = 0;
1131 if (size->IsCnsIntOrI())
1133 // If size is a constant, then it must be contained.
1134 assert(size->isContained());
1136 // If amount is zero then return null in regCnt
1137 size_t amount = size->gtIntCon.gtIconVal;
1140 instGen_Set_Reg_To_Zero(EA_PTRSIZE, regCnt);
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);
1154 stackAdjustment = 0;
1155 #if FEATURE_EH_FUNCLETS
1156 // If we have PSPsym, then need to re-locate it after localloc.
1159 stackAdjustment += STACK_ALIGN;
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);
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)
1172 assert((compiler->lvaOutgoingArgSpaceSize % STACK_ALIGN) == 0); // This must be true for the stack to remain
1174 inst_RV_IV(INS_add, REG_SPBASE, compiler->lvaOutgoingArgSpaceSize, EA_PTRSIZE);
1175 stackAdjustment += compiler->lvaOutgoingArgSpaceSize;
1179 // Put aligned allocation size to regCnt
1180 if (size->IsCnsIntOrI())
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);
1186 // For small allocations we will generate up to four stp instructions
1187 size_t cntStackAlignedWidthItems = (amount >> STACK_ALIGN_SHIFT);
1188 if (cntStackAlignedWidthItems <= 4)
1190 instGen_Set_Reg_To_Zero(EA_PTRSIZE, regCnt);
1192 while (cntStackAlignedWidthItems != 0)
1194 inst_IV(INS_push, (unsigned)genRegMask(regCnt));
1195 cntStackAlignedWidthItems -= 1;
1200 else if (!compiler->info.compInitMem && (amount < compiler->eeGetPageSize())) // must be < not <=
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);
1210 // regCnt will be the total number of bytes to locAlloc
1211 genSetRegToIcon(regCnt, amount, ((int)amount == amount) ? TYP_INT : TYP_LONG);
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));
1221 if (compiler->info.compInitMem)
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.
1227 regNumber regTmp = tree->ExtractTempReg();
1228 instGen_Set_Reg_To_Zero(EA_PTRSIZE, regTmp);
1231 BasicBlock* loop = genCreateTempLabel();
1232 genDefineTempLabel(loop);
1234 noway_assert(STACK_ALIGN == 8);
1235 inst_IV(INS_push, (unsigned)genRegMask(regTmp));
1236 inst_IV(INS_push, (unsigned)genRegMask(regTmp));
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);
1247 // At this point 'regCnt' is set to the total number of bytes to locAlloc.
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.
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
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
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
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
1279 regNumber regTmp = tree->ExtractTempReg();
1281 BasicBlock* loop = genCreateTempLabel();
1282 BasicBlock* done = genCreateTempLabel();
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);
1287 inst_JMP(EJ_vc, loop); // branch if the V flag is not set
1289 // Ups... Overflow, set regCnt to lowest possible value
1290 instGen_Set_Reg_To_Zero(EA_PTRSIZE, regCnt);
1292 genDefineTempLabel(loop);
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);
1297 // decrement SP by PAGE_SIZE
1298 getEmitter()->emitIns_R_R_I(INS_sub, EA_PTRSIZE, regTmp, REG_SPBASE, compiler->eeGetPageSize());
1300 getEmitter()->emitIns_R_R(INS_cmp, EA_PTRSIZE, regTmp, regCnt);
1301 emitJumpKind jmpLTU = genJumpKindForOper(GT_LT, CK_UNSIGNED);
1302 inst_JMP(jmpLTU, done);
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);
1307 // Jump to loop and tickle new stack address
1308 inst_JMP(EJ_jmp, loop);
1310 // Done with stack tickle loop
1311 genDefineTempLabel(done);
1313 // Now just move the final value to SP
1314 getEmitter()->emitIns_R_R(INS_mov, EA_PTRSIZE, REG_SPBASE, regCnt);
1318 // Re-adjust SP to allocate PSPSym and out-going arg area
1319 if (stackAdjustment != 0)
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);
1325 #if FEATURE_EH_FUNCLETS
1326 // Write PSPSym to its new location.
1329 assert(genIsValidIntReg(pspSymReg));
1330 getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, pspSymReg, compiler->lvaPSPSym, 0);
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);
1337 else // stackAdjustment == 0
1339 // Move the final value of SP to regCnt
1340 inst_RV_RV(INS_mov, regCnt, REG_SPBASE);
1344 if (endLabel != nullptr)
1345 genDefineTempLabel(endLabel);
1347 // Write the lvaLocAllocSPvar stack frame slot
1348 if (compiler->lvaLocAllocSPvar != BAD_VAR_NUM)
1350 getEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, regCnt, compiler->lvaLocAllocSPvar, 0);
1354 if (compiler->opts.compNeedStackProbes)
1356 genGenerateStackProbe();
1362 if (compiler->opts.compStackCheckOnRet)
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);
1371 genProduceReg(tree);
1374 //------------------------------------------------------------------------
1375 // genTableBasedSwitch: generate code for a switch statement based on a table of ip-relative offsets
1377 void CodeGen::genTableBasedSwitch(GenTree* treeNode)
1379 genConsumeOperands(treeNode->AsOp());
1380 regNumber idxReg = treeNode->gtOp.gtOp1->gtRegNum;
1381 regNumber baseReg = treeNode->gtOp.gtOp2->gtRegNum;
1383 getEmitter()->emitIns_R_ARX(INS_ldr, EA_4BYTE, REG_PC, baseReg, idxReg, TARGET_POINTER_SIZE, 0);
1386 //------------------------------------------------------------------------
1387 // genJumpTable: emits the table and an instruction to get the address of the first element
1389 void CodeGen::genJumpTable(GenTree* treeNode)
1391 noway_assert(compiler->compCurBB->bbJumpKind == BBJ_SWITCH);
1392 assert(treeNode->OperGet() == GT_JMPTABLE);
1394 unsigned jumpCount = compiler->compCurBB->bbJumpSwt->bbsCount;
1395 BasicBlock** jumpTable = compiler->compCurBB->bbJumpSwt->bbsDstTab;
1396 unsigned jmpTabBase;
1398 jmpTabBase = getEmitter()->emitBBTableDataGenBeg(jumpCount, false);
1400 JITDUMP("\n J_M%03u_DS%02u LABEL DWORD\n", Compiler::s_compMethodsCount, jmpTabBase);
1402 for (unsigned i = 0; i < jumpCount; i++)
1404 BasicBlock* target = *jumpTable++;
1405 noway_assert(target->bbFlags & BBF_JMP_TARGET);
1407 JITDUMP(" DD L_M%03u_BB%02u\n", Compiler::s_compMethodsCount, target->bbNum);
1409 getEmitter()->emitDataGenData(i, target);
1412 getEmitter()->emitDataGenEnd();
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);
1417 genProduceReg(treeNode);
1420 //------------------------------------------------------------------------
1421 // genGetInsForOper: Return instruction encoding of the operation tree.
1423 instruction CodeGen::genGetInsForOper(genTreeOps oper, var_types type)
1427 if (varTypeIsFloating(type))
1428 return CodeGen::ins_MathOp(oper, type);
1445 ins = INS_SHIFT_LEFT_LOGICAL;
1457 ins = INS_SHIFT_RIGHT_ARITHM;
1460 ins = INS_SHIFT_RIGHT_LOGICAL;
1484 ins = INS_SHIFT_LEFT_LOGICAL;
1487 ins = INS_SHIFT_RIGHT_LOGICAL;
1496 // Generates CpBlk code by performing a loop unroll
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)
1502 NYI_ARM("genCodeForCpBlkUnroll");
1505 // Generate code for InitBlk by performing a loop unroll
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)
1511 NYI_ARM("genCodeForInitBlkUnroll");
1514 void CodeGen::genCodeForStoreBlk(GenTreeBlk* blkOp)
1516 if (blkOp->gtBlkOpGcUnsafe)
1518 getEmitter()->emitDisableGC();
1520 bool isCopyBlk = blkOp->OperIsCopyBlkOp();
1522 switch (blkOp->gtBlkOpKind)
1524 case GenTreeBlk::BlkOpKindHelper:
1527 genCodeForCpBlk(blkOp);
1531 genCodeForInitBlk(blkOp);
1534 case GenTreeBlk::BlkOpKindUnroll:
1537 genCodeForCpBlkUnroll(blkOp);
1541 genCodeForInitBlkUnroll(blkOp);
1547 if (blkOp->gtBlkOpGcUnsafe)
1549 getEmitter()->emitEnableGC();
1553 //------------------------------------------------------------------------
1554 // genCodeForShiftLong: Generates the code sequence for a GenTree node that
1555 // represents a three operand bit shift or rotate operation (<<Hi, >>Lo).
1558 // tree - the bit shift node (that specifies the type of bit shift to perform).
1561 // a) All GenTrees are register allocated.
1562 // b) The shift-by-amount in tree->gtOp.gtOp2 is a contained constant
1564 void CodeGen::genCodeForShiftLong(GenTreePtr tree)
1566 // Only the non-RMW case here.
1567 genTreeOps oper = tree->OperGet();
1568 assert(oper == GT_LSH_HI || oper == GT_RSH_LO);
1570 GenTree* operand = tree->gtOp.gtOp1;
1571 assert(operand->OperGet() == GT_LONG);
1572 assert(operand->gtOp.gtOp1->isUsedFromReg());
1573 assert(operand->gtOp.gtOp2->isUsedFromReg());
1575 GenTree* operandLo = operand->gtGetOp1();
1576 GenTree* operandHi = operand->gtGetOp2();
1578 regNumber regLo = operandLo->gtRegNum;
1579 regNumber regHi = operandHi->gtRegNum;
1581 genConsumeOperands(tree->AsOp());
1583 var_types targetType = tree->TypeGet();
1584 instruction ins = genGetInsForOper(oper, targetType);
1586 GenTreePtr shiftBy = tree->gtGetOp2();
1588 assert(shiftBy->isContainedIntOrIImmed());
1590 unsigned int count = shiftBy->AsIntConCommon()->IconValue();
1592 regNumber regResult = (oper == GT_LSH_HI) ? regHi : regLo;
1594 if (regResult != tree->gtRegNum)
1596 inst_RV_RV(INS_mov, tree->gtRegNum, regResult, targetType);
1599 if (oper == GT_LSH_HI)
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);
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);
1613 genProduceReg(tree);
1616 //------------------------------------------------------------------------
1617 // genLeaInstruction: Produce code for a GT_LEA subnode.
1619 void CodeGen::genLeaInstruction(GenTreeAddrMode* lea)
1621 emitAttr size = emitTypeSize(lea);
1622 genConsumeOperands(lea);
1624 if (lea->Base() && lea->Index())
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);
1630 else if (lea->Base())
1632 regNumber baseReg = lea->Base()->gtRegNum;
1633 getEmitter()->emitIns_R_AR(INS_lea, size, lea->gtRegNum, baseReg, lea->gtOffset);
1635 else if (lea->Index())
1637 assert(!"Should we see a baseless address computation during CodeGen for ARM32?");
1643 //------------------------------------------------------------------------
1644 // genCompareLong: Generate code for comparing two longs when the result of the compare
1645 // is manifested in a register.
1648 // treeNode - the compare tree
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.
1658 void CodeGen::genCompareLong(GenTreePtr treeNode)
1660 assert(treeNode->OperIsCompare());
1662 GenTreeOp* tree = treeNode->AsOp();
1663 GenTreePtr op1 = tree->gtOp1;
1664 GenTreePtr op2 = tree->gtOp2;
1666 assert(varTypeIsLong(op1->TypeGet()));
1667 assert(varTypeIsLong(op2->TypeGet()));
1669 regNumber targetReg = treeNode->gtRegNum;
1671 genConsumeOperands(tree);
1673 GenTreePtr loOp1 = op1->gtGetOp1();
1674 GenTreePtr hiOp1 = op1->gtGetOp2();
1675 GenTreePtr loOp2 = op2->gtGetOp1();
1676 GenTreePtr hiOp2 = op2->gtGetOp2();
1678 // Create compare for the high parts
1679 instruction ins = INS_cmp;
1680 var_types cmpType = TYP_INT;
1681 emitAttr cmpAttr = emitTypeSize(cmpType);
1683 // Emit the compare instruction
1684 getEmitter()->emitInsBinary(ins, cmpAttr, hiOp1, hiOp2);
1686 // If the result is not being materialized in a register, we're done.
1687 if (targetReg == REG_NA)
1692 BasicBlock* labelTrue = genCreateTempLabel();
1693 BasicBlock* labelFalse = genCreateTempLabel();
1694 BasicBlock* labelNext = genCreateTempLabel();
1696 genJccLongHi(tree->gtOper, labelTrue, labelFalse, tree->IsUnsigned());
1697 getEmitter()->emitInsBinary(ins, cmpAttr, loOp1, loOp2);
1698 genJccLongLo(tree->gtOper, labelTrue, labelFalse);
1700 genDefineTempLabel(labelFalse);
1701 getEmitter()->emitIns_R_I(INS_mov, emitActualTypeSize(tree->gtType), tree->gtRegNum, 0);
1702 getEmitter()->emitIns_J(INS_b, labelNext);
1704 genDefineTempLabel(labelTrue);
1705 getEmitter()->emitIns_R_I(INS_mov, emitActualTypeSize(tree->gtType), tree->gtRegNum, 1);
1707 genDefineTempLabel(labelNext);
1709 genProduceReg(tree);
1712 void CodeGen::genJccLongHi(genTreeOps cmp, BasicBlock* jumpTrue, BasicBlock* jumpFalse, bool isUnsigned)
1716 jumpFalse->bbFlags |= BBF_JMP_TARGET | BBF_HAS_LABEL;
1722 inst_JMP(EJ_ne, jumpFalse);
1726 inst_JMP(EJ_ne, jumpTrue);
1733 inst_JMP(EJ_hi, jumpFalse);
1734 inst_JMP(EJ_lo, jumpTrue);
1738 inst_JMP(EJ_gt, jumpFalse);
1739 inst_JMP(EJ_lt, jumpTrue);
1747 inst_JMP(EJ_lo, jumpFalse);
1748 inst_JMP(EJ_hi, jumpTrue);
1752 inst_JMP(EJ_lt, jumpFalse);
1753 inst_JMP(EJ_gt, jumpTrue);
1758 noway_assert(!"expected a comparison operator");
1762 void CodeGen::genJccLongLo(genTreeOps cmp, BasicBlock* jumpTrue, BasicBlock* jumpFalse)
1767 inst_JMP(EJ_eq, jumpTrue);
1771 inst_JMP(EJ_ne, jumpTrue);
1775 inst_JMP(EJ_lo, jumpTrue);
1779 inst_JMP(EJ_ls, jumpTrue);
1783 inst_JMP(EJ_hs, jumpTrue);
1787 inst_JMP(EJ_hi, jumpTrue);
1791 noway_assert(!"expected comparison");
1795 //------------------------------------------------------------------------
1796 // genSetRegToCond: Generate code to materialize a condition into a register.
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
1802 // Return Value: none
1805 // The condition codes must already have been appropriately set.
1807 void CodeGen::genSetRegToCond(regNumber dstReg, GenTreePtr tree)
1809 // Emit code like that:
1819 CompareKind compareKind = ((tree->gtFlags & GTF_UNSIGNED) != 0) ? CK_UNSIGNED : CK_SIGNED;
1820 emitJumpKind jmpKind = genJumpKindForOper(tree->gtOper, compareKind);
1822 BasicBlock* labelTrue = genCreateTempLabel();
1823 getEmitter()->emitIns_J(emitter::emitJumpKindToIns(jmpKind), labelTrue);
1825 getEmitter()->emitIns_R_I(INS_mov, emitActualTypeSize(tree->gtType), dstReg, 0);
1827 BasicBlock* labelNext = genCreateTempLabel();
1828 getEmitter()->emitIns_J(INS_b, labelNext);
1830 genDefineTempLabel(labelTrue);
1831 getEmitter()->emitIns_R_I(INS_mov, emitActualTypeSize(tree->gtType), dstReg, 1);
1832 genDefineTempLabel(labelNext);
1835 //------------------------------------------------------------------------
1836 // genLongToIntCast: Generate code for long to int casts.
1839 // cast - The GT_CAST node
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.
1848 void CodeGen::genLongToIntCast(GenTree* cast)
1850 assert(cast->OperGet() == GT_CAST);
1852 GenTree* src = cast->gtGetOp1();
1853 noway_assert(src->OperGet() == GT_LONG);
1855 genConsumeRegs(src);
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;
1863 assert((dstType == TYP_INT) || (dstType == TYP_UINT));
1864 assert(genIsValidIntReg(loSrcReg));
1865 assert(genIsValidIntReg(hiSrcReg));
1866 assert(genIsValidIntReg(dstReg));
1868 if (cast->gtOverflow())
1871 // Generate an overflow check for [u]long to [u]int casts:
1873 // long -> int - check if the upper 33 bits are all 0 or all 1
1875 // ulong -> int - check if the upper 33 bits are all 0
1877 // long -> uint - check if the upper 32 bits are all 0
1878 // ulong -> uint - check if the upper 32 bits are all 0
1881 if ((srcType == TYP_LONG) && (dstType == TYP_INT))
1883 BasicBlock* allOne = genCreateTempLabel();
1884 BasicBlock* success = genCreateTempLabel();
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);
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);
1899 genDefineTempLabel(success);
1903 if ((srcType == TYP_ULONG) && (dstType == TYP_INT))
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);
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);
1916 if (dstReg != loSrcReg)
1918 inst_RV_RV(INS_mov, dstReg, loSrcReg, TYP_INT, EA_4BYTE);
1921 genProduceReg(cast);
1924 //------------------------------------------------------------------------
1925 // genIntToFloatCast: Generate code to cast an int/long to float/double
1928 // treeNode - The GT_CAST node
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.
1938 void CodeGen::genIntToFloatCast(GenTreePtr treeNode)
1940 // int --> float/double conversions are always non-overflow ones
1941 assert(treeNode->OperGet() == GT_CAST);
1942 assert(!treeNode->gtOverflow());
1944 regNumber targetReg = treeNode->gtRegNum;
1945 assert(genIsValidFloatReg(targetReg));
1947 GenTreePtr op1 = treeNode->gtOp.gtOp1;
1948 assert(!op1->isContained()); // Cannot be contained
1949 assert(genIsValidIntReg(op1->gtRegNum)); // Must be a valid int reg.
1951 var_types dstType = treeNode->CastToType();
1952 var_types srcType = op1->TypeGet();
1953 assert(!varTypeIsFloating(srcType) && varTypeIsFloating(dstType));
1955 // force the srcType to unsigned if GT_UNSIGNED flag is set
1956 if (treeNode->gtFlags & GTF_UNSIGNED)
1958 srcType = genUnsignedType(srcType);
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.
1965 emitAttr srcSize = EA_ATTR(genTypeSize(srcType));
1966 noway_assert((srcSize == EA_4BYTE) || (srcSize == EA_8BYTE));
1968 instruction insVcvt = INS_invalid;
1970 if (dstType == TYP_DOUBLE)
1972 if (srcSize == EA_4BYTE)
1974 insVcvt = (varTypeIsUnsigned(srcType)) ? INS_vcvt_u2d : INS_vcvt_i2d;
1978 assert(srcSize == EA_8BYTE);
1979 NYI_ARM("Casting int64/uint64 to double in genIntToFloatCast");
1984 assert(dstType == TYP_FLOAT);
1985 if (srcSize == EA_4BYTE)
1987 insVcvt = (varTypeIsUnsigned(srcType)) ? INS_vcvt_u2f : INS_vcvt_i2f;
1991 assert(srcSize == EA_8BYTE);
1992 NYI_ARM("Casting int64/uint64 to float in genIntToFloatCast");
1996 genConsumeOperands(treeNode->AsOp());
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);
2002 genProduceReg(treeNode);
2005 //------------------------------------------------------------------------
2006 // genFloatToIntCast: Generate code to cast float/double to int/long
2009 // treeNode - The GT_CAST node
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
2019 void CodeGen::genFloatToIntCast(GenTreePtr treeNode)
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());
2026 regNumber targetReg = treeNode->gtRegNum;
2027 assert(genIsValidIntReg(targetReg)); // Must be a valid int reg.
2029 GenTreePtr op1 = treeNode->gtOp.gtOp1;
2030 assert(!op1->isContained()); // Cannot be contained
2031 assert(genIsValidFloatReg(op1->gtRegNum)); // Must be a valid float reg.
2033 var_types dstType = treeNode->CastToType();
2034 var_types srcType = op1->TypeGet();
2035 assert(varTypeIsFloating(srcType) && !varTypeIsFloating(dstType));
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.
2041 emitAttr dstSize = EA_ATTR(genTypeSize(dstType));
2042 noway_assert((dstSize == EA_4BYTE) || (dstSize == EA_8BYTE));
2044 instruction insVcvt = INS_invalid;
2046 if (srcType == TYP_DOUBLE)
2048 if (dstSize == EA_4BYTE)
2050 insVcvt = (varTypeIsUnsigned(dstType)) ? INS_vcvt_d2u : INS_vcvt_d2i;
2054 assert(dstSize == EA_8BYTE);
2055 NYI_ARM("Casting double to int64/uint64 in genIntToFloatCast");
2060 assert(srcType == TYP_FLOAT);
2061 if (dstSize == EA_4BYTE)
2063 insVcvt = (varTypeIsUnsigned(dstType)) ? INS_vcvt_f2u : INS_vcvt_f2i;
2067 assert(dstSize == EA_8BYTE);
2068 NYI_ARM("Casting float to int64/uint64 in genIntToFloatCast");
2072 genConsumeOperands(treeNode->AsOp());
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);
2078 genProduceReg(treeNode);
2081 //------------------------------------------------------------------------
2082 // genEmitHelperCall: Emit a call to a helper function.
2084 void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, regNumber callTargetReg /*= REG_NA */)
2086 // Can we call the helper function directly
2088 void *addr = NULL, **pAddr = NULL;
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))
2096 addr = compiler->compProfilerMethHnd;
2101 addr = compiler->compGetHelperFtn((CorInfoHelpFunc)helper, (void**)&pAddr);
2104 if (!addr || !arm_Valid_Imm_For_BL((ssize_t)addr))
2106 if (callTargetReg == REG_NA)
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;
2113 // Load the address into a register and call through a register
2116 instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, callTargetReg, (ssize_t)addr);
2120 getEmitter()->emitIns_R_AI(INS_ldr, EA_PTR_DSP_RELOC, callTargetReg, (ssize_t)pAddr);
2121 regTracker.rsTrackRegTrash(callTargetReg);
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
2132 emitter::emitNoGChelper(helper),
2133 (CorInfoHelpFunc)helper == CORINFO_HELP_PROF_FCN_LEAVE);
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 */
2142 emitter::emitNoGChelper(helper),
2143 (CorInfoHelpFunc)helper == CORINFO_HELP_PROF_FCN_LEAVE);
2146 regTracker.rsTrashRegSet(RBM_CALLEE_TRASH);
2147 regTracker.rsTrashRegsForGCInterruptability();
2150 //------------------------------------------------------------------------
2151 // genStoreLongLclVar: Generate code to store a non-enregistered long lclVar
2154 // treeNode - A TYP_LONG lclVar node.
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.
2163 void CodeGen::genStoreLongLclVar(GenTree* treeNode)
2165 emitter* emit = getEmitter();
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);
2176 if (op1->OperGet() == GT_LONG)
2178 // Definitions of register candidates will have been lowered to 2 int lclVars.
2179 assert(!treeNode->InReg());
2181 GenTreePtr loVal = op1->gtGetOp1();
2182 GenTreePtr hiVal = op1->gtGetOp2();
2184 // NYI: Contained immediates.
2185 NYI_IF((loVal->gtRegNum == REG_NA) || (hiVal->gtRegNum == REG_NA),
2186 "Store of long lclVar with contained immediate");
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));
2191 else if (op1->OperGet() == GT_MUL_LONG)
2193 assert((op1->gtFlags & GTF_MUL_64RSLT) != 0);
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));
2202 #endif // _TARGET_ARM_
2204 #endif // !LEGACY_BACKEND