GenTreePtr op1 = treeNode->gtGetOp1();
var_types targetType = treeNode->TypeGet();
+ // A void GT_RETFILT is the end of a finally. For non-void filter returns we need to load the result in the return
+ // register, if it's not already there. The processing is the same as GT_RETURN. For filters, the IL spec says the
+ // result is type int32. Further, the only legal values are 0 or 1; the use of other values is "undefined".
+ assert(!treeNode->OperIs(GT_RETFILT) || (targetType == TYP_VOID) || (targetType == TYP_INT));
+
#ifdef DEBUG
if (targetType == TYP_VOID)
{
}
//------------------------------------------------------------------------
-// genCodeForTreeNode Generate code for a single node in the tree.
-//
-// Preconditions:
-// All operands have been evaluated.
-//
-void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
-{
- regNumber targetReg = treeNode->gtRegNum;
- var_types targetType = treeNode->TypeGet();
- emitter* emit = getEmitter();
-
-#ifdef DEBUG
- lastConsumedNode = nullptr;
- if (compiler->verbose)
- {
- unsigned seqNum = treeNode->gtSeqNum; // Useful for setting a conditional break in Visual Studio
- compiler->gtDispLIRNode(treeNode, "Generating: ");
- }
-#endif
-
- // contained nodes are part of their parents for codegen purposes
- // ex : immediates, most LEAs
- if (treeNode->isContained())
- {
- return;
- }
-
- switch (treeNode->gtOper)
- {
- case GT_LCLHEAP:
- genLclHeap(treeNode);
- break;
-
- case GT_CNS_INT:
- case GT_CNS_DBL:
- genSetRegToConst(targetReg, targetType, treeNode);
- genProduceReg(treeNode);
- break;
-
- case GT_NOT:
- case GT_NEG:
- genCodeForNegNot(treeNode);
- break;
-
- case GT_OR:
- case GT_XOR:
- case GT_AND:
- assert(varTypeIsIntegralOrI(treeNode));
- __fallthrough;
-
- case GT_ADD_LO:
- case GT_ADD_HI:
- case GT_SUB_LO:
- case GT_SUB_HI:
- case GT_ADD:
- case GT_SUB:
- case GT_MUL:
- genConsumeOperands(treeNode->AsOp());
- genCodeForBinary(treeNode);
- break;
-
- case GT_LSH:
- case GT_RSH:
- case GT_RSZ:
- case GT_ROR:
- genCodeForShift(treeNode);
- break;
-
- case GT_LSH_HI:
- case GT_RSH_LO:
- genCodeForShiftLong(treeNode);
- break;
-
- case GT_CAST:
- // Cast is never contained (?)
- noway_assert(targetReg != REG_NA);
-
- if (varTypeIsFloating(targetType) && varTypeIsFloating(treeNode->gtOp.gtOp1))
- {
- // Casts float/double <--> double/float
- genFloatToFloatCast(treeNode);
- }
- else if (varTypeIsFloating(treeNode->gtOp.gtOp1))
- {
- // Casts float/double --> int32/int64
- genFloatToIntCast(treeNode);
- }
- else if (varTypeIsFloating(targetType))
- {
- // Casts int32/uint32/int64/uint64 --> float/double
- genIntToFloatCast(treeNode);
- }
- else
- {
- // Casts int <--> int
- genIntToIntCast(treeNode);
- }
- // The per-case functions call genProduceReg()
- break;
-
- case GT_LCL_FLD_ADDR:
- case GT_LCL_VAR_ADDR:
- {
- // Address of a local var. This by itself should never be allocated a register.
- // If it is worth storing the address in a register then it should be cse'ed into
- // a temp and that would be allocated a register.
- noway_assert(targetType == TYP_BYREF);
- noway_assert(!treeNode->InReg());
-
- inst_RV_TT(INS_lea, targetReg, treeNode, 0, EA_BYREF);
- }
- genProduceReg(treeNode);
- break;
-
- case GT_LCL_FLD:
- genCodeForLclFld(treeNode->AsLclFld());
- break;
-
- case GT_LCL_VAR:
- genCodeForLclVar(treeNode->AsLclVar());
- break;
-
- case GT_STORE_LCL_FLD:
- genCodeForStoreLclFld(treeNode->AsLclFld());
- break;
-
- case GT_STORE_LCL_VAR:
- genCodeForStoreLclVar(treeNode->AsLclVar());
- break;
-
- case GT_RETFILT:
- // A void GT_RETFILT is the end of a finally. For non-void filter returns we need to load the result in
- // the return register, if it's not already there. The processing is the same as GT_RETURN.
- if (targetType != TYP_VOID)
- {
- // For filters, the IL spec says the result is type int32. Further, the only specified legal values
- // are 0 or 1, with the use of other values "undefined".
- assert(targetType == TYP_INT);
- }
-
- __fallthrough;
-
- case GT_RETURN:
- genReturn(treeNode);
- break;
-
- case GT_LEA:
- // if we are here, it is the case where there is an LEA that cannot
- // be folded into a parent instruction
- genLeaInstruction(treeNode->AsAddrMode());
- break;
-
- case GT_IND:
- genConsumeAddress(treeNode->AsIndir()->Addr());
- emit->emitInsLoadStoreOp(ins_Load(targetType), emitTypeSize(treeNode), targetReg, treeNode->AsIndir());
- genProduceReg(treeNode);
- break;
-
- case GT_MOD:
- case GT_UDIV:
- case GT_UMOD:
- // We shouldn't be seeing GT_MOD on float/double args as it should get morphed into a
- // helper call by front-end. Similarly we shouldn't be seeing GT_UDIV and GT_UMOD
- // on float/double args.
- noway_assert(!varTypeIsFloating(treeNode));
- __fallthrough;
-
- case GT_DIV:
- {
- genConsumeOperands(treeNode->AsOp());
-
- noway_assert(targetReg != REG_NA);
-
- GenTreePtr dst = treeNode;
- GenTreePtr src1 = treeNode->gtGetOp1();
- GenTreePtr src2 = treeNode->gtGetOp2();
- instruction ins = genGetInsForOper(treeNode->OperGet(), targetType);
- emitAttr attr = emitTypeSize(treeNode);
- regNumber result = REG_NA;
-
- // dst can only be a reg
- assert(!dst->isContained());
-
- // src can be only reg
- assert(!src1->isContained() || !src2->isContained());
-
- if (varTypeIsFloating(targetType))
- {
- // Floating point divide never raises an exception
-
- emit->emitIns_R_R_R(ins, attr, dst->gtRegNum, src1->gtRegNum, src2->gtRegNum);
- }
- else // an signed integer divide operation
- {
- // TODO-ARM-Bug: handle zero division exception.
-
- emit->emitIns_R_R_R(ins, attr, dst->gtRegNum, src1->gtRegNum, src2->gtRegNum);
- }
-
- genProduceReg(treeNode);
- }
- break;
-
- case GT_INTRINSIC:
- genIntrinsic(treeNode);
- break;
-
- case GT_EQ:
- case GT_NE:
- case GT_LT:
- case GT_LE:
- case GT_GE:
- case GT_GT:
- genCodeForCompare(treeNode->AsOp());
- break;
-
- case GT_JTRUE:
- genCodeForJumpTrue(treeNode);
- break;
-
- case GT_JCC:
- {
- GenTreeJumpCC* jcc = treeNode->AsJumpCC();
-
- assert(compiler->compCurBB->bbJumpKind == BBJ_COND);
-
- CompareKind compareKind = ((jcc->gtFlags & GTF_UNSIGNED) != 0) ? CK_UNSIGNED : CK_SIGNED;
- emitJumpKind jumpKind = genJumpKindForOper(jcc->gtCondition, compareKind);
-
- inst_JMP(jumpKind, compiler->compCurBB->bbJumpDest);
- }
- break;
-
- case GT_RETURNTRAP:
- genCodeForReturnTrap(treeNode->AsOp());
- break;
-
- case GT_STOREIND:
- genCodeForStoreInd(treeNode->AsStoreInd());
- break;
-
- case GT_COPY:
- // This is handled at the time we call genConsumeReg() on the GT_COPY
- break;
-
- case GT_LIST:
- case GT_FIELD_LIST:
- case GT_ARGPLACE:
- // Nothing to do
- break;
-
- case GT_PUTARG_STK:
- genPutArgStk(treeNode->AsPutArgStk());
- break;
-
- case GT_PUTARG_REG:
- genPutArgReg(treeNode->AsOp());
- break;
-
- case GT_CALL:
- genCallInstruction(treeNode->AsCall());
- break;
-
- case GT_LOCKADD:
- case GT_XCHG:
- case GT_XADD:
- genLockedInstructions(treeNode->AsOp());
- break;
-
- case GT_MEMORYBARRIER:
- instGen_MemoryBarrier();
- break;
-
- case GT_CMPXCHG:
- NYI("GT_CMPXCHG");
- genProduceReg(treeNode);
- break;
-
- case GT_RELOAD:
- // do nothing - reload is just a marker.
- // The parent node will call genConsumeReg on this which will trigger the unspill of this node's child
- // into the register specified in this node.
- break;
-
- case GT_NOP:
- break;
-
- case GT_NO_OP:
- if (treeNode->gtFlags & GTF_NO_OP_NO)
- {
- noway_assert(!"GTF_NO_OP_NO should not be set");
- }
- else
- {
- instGen(INS_nop);
- }
- break;
-
- case GT_ARR_BOUNDS_CHECK:
- genRangeCheck(treeNode);
- break;
-
- case GT_PHYSREG:
- if (treeNode->gtRegNum != treeNode->AsPhysReg()->gtSrcReg)
- {
- inst_RV_RV(INS_mov, treeNode->gtRegNum, treeNode->AsPhysReg()->gtSrcReg, targetType);
-
- genTransferRegGCState(treeNode->gtRegNum, treeNode->AsPhysReg()->gtSrcReg);
- }
- break;
-
- case GT_PHYSREGDST:
- break;
-
- case GT_NULLCHECK:
- {
- assert(!treeNode->gtOp.gtOp1->isContained());
- regNumber addrReg = genConsumeReg(treeNode->gtOp.gtOp1);
- emit->emitIns_R_R_I(INS_ldr, EA_4BYTE, targetReg, addrReg, 0);
- }
- break;
-
- case GT_CATCH_ARG:
-
- noway_assert(handlerGetsXcptnObj(compiler->compCurBB->bbCatchTyp));
-
- /* Catch arguments get passed in a register. genCodeForBBlist()
- would have marked it as holding a GC object, but not used. */
-
- noway_assert(gcInfo.gcRegGCrefSetCur & RBM_EXCEPTION_OBJECT);
- genConsumeReg(treeNode);
- break;
-
- case GT_PINVOKE_PROLOG:
- noway_assert(((gcInfo.gcRegGCrefSetCur | gcInfo.gcRegByrefSetCur) & ~fullIntArgRegMask()) == 0);
-
- // the runtime side requires the codegen here to be consistent
- emit->emitDisableRandomNops();
- break;
-
- case GT_LABEL:
- genPendingCallLabel = genCreateTempLabel();
- treeNode->gtLabel.gtLabBB = genPendingCallLabel;
- emit->emitIns_J_R(INS_adr, EA_PTRSIZE, genPendingCallLabel, treeNode->gtRegNum);
- break;
-
- case GT_CLS_VAR_ADDR:
- emit->emitIns_R_C(INS_lea, EA_PTRSIZE, targetReg, treeNode->gtClsVar.gtClsVarHnd, 0);
- genProduceReg(treeNode);
- break;
-
- case GT_STORE_DYN_BLK:
- case GT_STORE_BLK:
- genCodeForStoreBlk(treeNode->AsBlk());
- break;
-
- case GT_JMPTABLE:
- genJumpTable(treeNode);
- break;
-
- case GT_SWITCH_TABLE:
- genTableBasedSwitch(treeNode);
- break;
-
- case GT_ARR_INDEX:
- genCodeForArrIndex(treeNode->AsArrIndex());
- break;
-
- case GT_ARR_OFFSET:
- genCodeForArrOffset(treeNode->AsArrOffs());
- break;
-
- case GT_IL_OFFSET:
- // Do nothing; these nodes are simply markers for debug info.
- break;
-
- default:
- {
-#ifdef DEBUG
- char message[256];
- _snprintf_s(message, _countof(message), _TRUNCATE, "NYI: Unimplemented node type %s",
- GenTree::NodeName(treeNode->OperGet()));
- NYIRAW(message);
-#else
- NYI("unimplemented node");
-#endif
- }
- break;
- }
-}
-
-//------------------------------------------------------------------------
// genLockedInstructions: Generate code for the locked operations.
//
// Notes:
NYI_ARM("genCodeForInitBlkUnroll");
}
-void CodeGen::genCodeForStoreBlk(GenTreeBlk* blkOp)
-{
- if (blkOp->gtBlkOpGcUnsafe)
- {
- getEmitter()->emitDisableGC();
- }
- bool isCopyBlk = blkOp->OperIsCopyBlkOp();
-
- switch (blkOp->gtBlkOpKind)
- {
- case GenTreeBlk::BlkOpKindHelper:
- if (isCopyBlk)
- {
- genCodeForCpBlk(blkOp);
- }
- else
- {
- genCodeForInitBlk(blkOp);
- }
- break;
- case GenTreeBlk::BlkOpKindUnroll:
- if (isCopyBlk)
- {
- genCodeForCpBlkUnroll(blkOp);
- }
- else
- {
- genCodeForInitBlkUnroll(blkOp);
- }
- break;
- default:
- unreached();
- }
- if (blkOp->gtBlkOpGcUnsafe)
- {
- getEmitter()->emitEnableGC();
- }
-}
-
//------------------------------------------------------------------------
// genCodeForNegNot: Produce code for a GT_NEG/GT_NOT node.
//
}
//------------------------------------------------------------------------
+// genCodeForDivMod: Produce code for a GT_DIV/GT_UDIV/GT_MOD/GT_UMOD node.
+//
+// Arguments:
+// tree - the node
+//
+void CodeGen::genCodeForDivMod(GenTreeOp* tree)
+{
+ assert(tree->OperIs(GT_DIV, GT_UDIV, GT_MOD, GT_UMOD));
+
+ // We shouldn't be seeing GT_MOD on float/double args as it should get morphed into a
+ // helper call by front-end. Similarly we shouldn't be seeing GT_UDIV and GT_UMOD
+ // on float/double args.
+ noway_assert(tree->OperIs(GT_DIV) || !varTypeIsFloating(tree));
+
+ var_types targetType = tree->TypeGet();
+ regNumber targetReg = tree->gtRegNum;
+ emitter* emit = getEmitter();
+
+ genConsumeOperands(tree);
+
+ noway_assert(targetReg != REG_NA);
+
+ GenTreePtr dst = tree;
+ GenTreePtr src1 = tree->gtGetOp1();
+ GenTreePtr src2 = tree->gtGetOp2();
+ instruction ins = genGetInsForOper(tree->OperGet(), targetType);
+ emitAttr attr = emitTypeSize(tree);
+ regNumber result = REG_NA;
+
+ // dst can only be a reg
+ assert(!dst->isContained());
+
+ // src can be only reg
+ assert(!src1->isContained() || !src2->isContained());
+
+ if (varTypeIsFloating(targetType))
+ {
+ // Floating point divide never raises an exception
+
+ emit->emitIns_R_R_R(ins, attr, dst->gtRegNum, src1->gtRegNum, src2->gtRegNum);
+ }
+ else // an signed integer divide operation
+ {
+ // TODO-ARM-Bug: handle zero division exception.
+
+ emit->emitIns_R_R_R(ins, attr, dst->gtRegNum, src1->gtRegNum, src2->gtRegNum);
+ }
+
+ genProduceReg(tree);
+}
+
+//------------------------------------------------------------------------
// genCodeForCompare: Produce code for a GT_EQ/GT_NE/GT_LT/GT_LE/GT_GE/GT_GT node.
//
// Arguments:
}
//------------------------------------------------------------------------
+// genCodeForJcc: Produce code for a GT_JCC node.
+//
+// Arguments:
+// tree - the node
+//
+void CodeGen::genCodeForJcc(GenTreeJumpCC* tree)
+{
+ assert(compiler->compCurBB->bbJumpKind == BBJ_COND);
+
+ CompareKind compareKind = ((tree->gtFlags & GTF_UNSIGNED) != 0) ? CK_UNSIGNED : CK_SIGNED;
+ emitJumpKind jumpKind = genJumpKindForOper(tree->gtCondition, compareKind);
+
+ inst_JMP(jumpKind, compiler->compCurBB->bbJumpDest);
+}
+
+//------------------------------------------------------------------------
// genCodeForReturnTrap: Produce code for a GT_RETURNTRAP node.
//
// Arguments:
{
inst_RV_RV(INS_mov, targetReg, REG_RDX, targetType);
}
+
+ genProduceReg(treeNode);
#else // !0
NYI("genCodeForMulHi");
#endif // !0
}
-// generate code for a DIV or MOD operation
-//
-void CodeGen::genCodeForDivMod(GenTreeOp* treeNode)
-{
- // unused on ARM64
-}
-
// Generate code for ADD, SUB, MUL, DIV, UDIV, AND, OR and XOR
// This method is expected to have called genConsumeOperands() before calling it.
void CodeGen::genCodeForBinary(GenTree* treeNode)
GenTreePtr op1 = treeNode->gtGetOp1();
var_types targetType = treeNode->TypeGet();
+ // A void GT_RETFILT is the end of a finally. For non-void filter returns we need to load the result in the return
+ // register, if it's not already there. The processing is the same as GT_RETURN. For filters, the IL spec says the
+ // result is type int32. Further, the only legal values are 0 or 1; the use of other values is "undefined".
+ assert(!treeNode->OperIs(GT_RETFILT) || (targetType == TYP_VOID) || (targetType == TYP_INT));
+
#ifdef DEBUG
if (targetType == TYP_VOID)
{
#endif
}
-/*****************************************************************************
- *
- * Generate code for a single node in the tree.
- * Preconditions: All operands have been evaluated
- *
- */
-void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
-{
- regNumber targetReg = treeNode->gtRegNum;
- var_types targetType = treeNode->TypeGet();
- emitter* emit = getEmitter();
-
-#ifdef DEBUG
- // Validate that all the operands for the current node are consumed in order.
- // This is important because LSRA ensures that any necessary copies will be
- // handled correctly.
- lastConsumedNode = nullptr;
- if (compiler->verbose)
- {
- unsigned seqNum = treeNode->gtSeqNum; // Useful for setting a conditional break in Visual Studio
- compiler->gtDispLIRNode(treeNode, "Generating: ");
- }
-#endif // DEBUG
-
- // Is this a node whose value is already in a register? LSRA denotes this by
- // setting the GTF_REUSE_REG_VAL flag.
- if (treeNode->IsReuseRegVal())
- {
- // For now, this is only used for constant nodes.
- assert((treeNode->OperGet() == GT_CNS_INT) || (treeNode->OperGet() == GT_CNS_DBL));
- JITDUMP(" TreeNode is marked ReuseReg\n");
- return;
- }
-
- // contained nodes are part of their parents for codegen purposes
- // ex : immediates, most LEAs
- if (treeNode->isContained())
- {
- return;
- }
-
- switch (treeNode->gtOper)
- {
- case GT_START_NONGC:
- getEmitter()->emitDisableGC();
- break;
-
- case GT_PROF_HOOK:
- // We should be seeing this only if profiler hook is needed
- noway_assert(compiler->compIsProfilerHookNeeded());
-
-#ifdef PROFILING_SUPPORTED
- // Right now this node is used only for tail calls. In future if
- // we intend to use it for Enter or Leave hooks, add a data member
- // to this node indicating the kind of profiler hook. For example,
- // helper number can be used.
- genProfilingLeaveCallback(CORINFO_HELP_PROF_FCN_TAILCALL);
-#endif // PROFILING_SUPPORTED
- break;
-
- case GT_LCLHEAP:
- genLclHeap(treeNode);
- break;
-
- case GT_CNS_INT:
- case GT_CNS_DBL:
- genSetRegToConst(targetReg, targetType, treeNode);
- genProduceReg(treeNode);
- break;
-
- case GT_NOT:
- case GT_NEG:
- genCodeForNegNot(treeNode);
- break;
-
- case GT_DIV:
- case GT_UDIV:
- genConsumeOperands(treeNode->AsOp());
-
- if (varTypeIsFloating(targetType))
- {
- // Floating point divide never raises an exception
- genCodeForBinary(treeNode);
- }
- else // an integer divide operation
- {
- GenTreePtr divisorOp = treeNode->gtGetOp2();
- emitAttr size = EA_ATTR(genTypeSize(genActualType(treeNode->TypeGet())));
-
- if (divisorOp->IsIntegralConst(0))
- {
- // We unconditionally throw a divide by zero exception
- genJumpToThrowHlpBlk(EJ_jmp, SCK_DIV_BY_ZERO);
-
- // We still need to call genProduceReg
- genProduceReg(treeNode);
- }
- else // the divisor is not the constant zero
- {
- regNumber divisorReg = divisorOp->gtRegNum;
-
- // Generate the require runtime checks for GT_DIV or GT_UDIV
- if (treeNode->gtOper == GT_DIV)
- {
- BasicBlock* sdivLabel = genCreateTempLabel();
-
- // Two possible exceptions:
- // (AnyVal / 0) => DivideByZeroException
- // (MinInt / -1) => ArithmeticException
- //
- bool checkDividend = true;
-
- // Do we have an immediate for the 'divisorOp'?
- //
- if (divisorOp->IsCnsIntOrI())
- {
- GenTreeIntConCommon* intConstTree = divisorOp->AsIntConCommon();
- ssize_t intConstValue = intConstTree->IconValue();
- assert(intConstValue != 0); // already checked above by IsIntegralConst(0))
- if (intConstValue != -1)
- {
- checkDividend = false; // We statically know that the dividend is not -1
- }
- }
- else // insert check for divison by zero
- {
- // Check if the divisor is zero throw a DivideByZeroException
- emit->emitIns_R_I(INS_cmp, size, divisorReg, 0);
- emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED);
- genJumpToThrowHlpBlk(jmpEqual, SCK_DIV_BY_ZERO);
- }
-
- if (checkDividend)
- {
- // Check if the divisor is not -1 branch to 'sdivLabel'
- emit->emitIns_R_I(INS_cmp, size, divisorReg, -1);
-
- emitJumpKind jmpNotEqual = genJumpKindForOper(GT_NE, CK_SIGNED);
- inst_JMP(jmpNotEqual, sdivLabel);
- // If control flow continues past here the 'divisorReg' is known to be -1
-
- regNumber dividendReg = treeNode->gtGetOp1()->gtRegNum;
- // At this point the divisor is known to be -1
- //
- // Issue the 'adds zr, dividendReg, dividendReg' instruction
- // this will set both the Z and V flags only when dividendReg is MinInt
- //
- emit->emitIns_R_R_R(INS_adds, size, REG_ZR, dividendReg, dividendReg);
- inst_JMP(jmpNotEqual, sdivLabel); // goto sdiv if the Z flag is clear
- genJumpToThrowHlpBlk(EJ_vs, SCK_ARITH_EXCPN); // if the V flags is set throw
- // ArithmeticException
-
- genDefineTempLabel(sdivLabel);
- }
- genCodeForBinary(treeNode); // Generate the sdiv instruction
- }
- else // (treeNode->gtOper == GT_UDIV)
- {
- // Only one possible exception
- // (AnyVal / 0) => DivideByZeroException
- //
- // Note that division by the constant 0 was already checked for above by the
- // op2->IsIntegralConst(0) check
- //
- if (!divisorOp->IsCnsIntOrI())
- {
- // divisorOp is not a constant, so it could be zero
- //
- emit->emitIns_R_I(INS_cmp, size, divisorReg, 0);
- emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED);
- genJumpToThrowHlpBlk(jmpEqual, SCK_DIV_BY_ZERO);
- }
- genCodeForBinary(treeNode);
- }
- }
- }
- break;
-
- case GT_OR:
- case GT_XOR:
- case GT_AND:
- assert(varTypeIsIntegralOrI(treeNode));
- __fallthrough;
- case GT_ADD:
- case GT_SUB:
- case GT_MUL:
- genConsumeOperands(treeNode->AsOp());
- genCodeForBinary(treeNode);
- break;
-
- case GT_LSH:
- case GT_RSH:
- case GT_RSZ:
- case GT_ROR:
- genCodeForShift(treeNode);
- // genCodeForShift() calls genProduceReg()
- break;
-
- case GT_CAST:
- if (varTypeIsFloating(targetType) && varTypeIsFloating(treeNode->gtOp.gtOp1))
- {
- // Casts float/double <--> double/float
- genFloatToFloatCast(treeNode);
- }
- else if (varTypeIsFloating(treeNode->gtOp.gtOp1))
- {
- // Casts float/double --> int32/int64
- genFloatToIntCast(treeNode);
- }
- else if (varTypeIsFloating(targetType))
- {
- // Casts int32/uint32/int64/uint64 --> float/double
- genIntToFloatCast(treeNode);
- }
- else
- {
- // Casts int <--> int
- genIntToIntCast(treeNode);
- }
- // The per-case functions call genProduceReg()
- break;
-
- case GT_LCL_FLD_ADDR:
- case GT_LCL_VAR_ADDR:
- // Address of a local var. This by itself should never be allocated a register.
- // If it is worth storing the address in a register then it should be cse'ed into
- // a temp and that would be allocated a register.
- noway_assert(targetType == TYP_BYREF);
- noway_assert(!treeNode->InReg());
-
- inst_RV_TT(INS_lea, targetReg, treeNode, 0, EA_BYREF);
- genProduceReg(treeNode);
- break;
-
- case GT_LCL_FLD:
- genCodeForLclFld(treeNode->AsLclFld());
- break;
-
- case GT_LCL_VAR:
- genCodeForLclVar(treeNode->AsLclVar());
- break;
-
- case GT_STORE_LCL_FLD:
- genCodeForStoreLclFld(treeNode->AsLclFld());
- break;
-
- case GT_STORE_LCL_VAR:
- genCodeForStoreLclVar(treeNode->AsLclVar());
- break;
-
- case GT_RETFILT:
- // A void GT_RETFILT is the end of a finally. For non-void filter returns we need to load the result in
- // the return register, if it's not already there. The processing is the same as GT_RETURN.
- if (targetType != TYP_VOID)
- {
- // For filters, the IL spec says the result is type int32. Further, the only specified legal values
- // are 0 or 1, with the use of other values "undefined".
- assert(targetType == TYP_INT);
- }
-
- __fallthrough;
-
- case GT_RETURN:
- genReturn(treeNode);
- break;
-
- case GT_LEA:
- // if we are here, it is the case where there is an LEA that cannot
- // be folded into a parent instruction
- genLeaInstruction(treeNode->AsAddrMode());
- break;
-
- case GT_IND:
- genConsumeAddress(treeNode->AsIndir()->Addr());
- emit->emitInsLoadStoreOp(ins_Load(targetType), emitTypeSize(treeNode), targetReg, treeNode->AsIndir());
- genProduceReg(treeNode);
- break;
-
- case GT_MULHI:
- genCodeForMulHi(treeNode->AsOp());
- genProduceReg(treeNode);
- break;
-
- case GT_MOD:
- case GT_UMOD:
- // Integer MOD should have been morphed into a sequence of sub, mul, div in fgMorph.
- //
- // We shouldn't be seeing GT_MOD on float/double as it is morphed into a helper call by front-end.
- noway_assert(!"Codegen for GT_MOD/GT_UMOD");
- break;
-
- case GT_INTRINSIC:
- genIntrinsic(treeNode);
- break;
-
-#ifdef FEATURE_SIMD
- case GT_SIMD:
- genSIMDIntrinsic(treeNode->AsSIMD());
- break;
-#endif // FEATURE_SIMD
-
- case GT_CKFINITE:
- genCkfinite(treeNode);
- break;
-
- case GT_EQ:
- case GT_NE:
- case GT_LT:
- case GT_LE:
- case GT_GE:
- case GT_GT:
- genCodeForCompare(treeNode->AsOp());
- break;
-
- case GT_JTRUE:
- genCodeForJumpTrue(treeNode);
- break;
-
- case GT_RETURNTRAP:
- genCodeForReturnTrap(treeNode->AsOp());
- break;
-
- case GT_STOREIND:
- genCodeForStoreInd(treeNode->AsStoreInd());
- break;
-
- case GT_COPY:
- // This is handled at the time we call genConsumeReg() on the GT_COPY
- break;
-
- case GT_SWAP:
- genCodeForSwap(treeNode->AsOp());
- break;
-
- case GT_LIST:
- case GT_FIELD_LIST:
- case GT_ARGPLACE:
- // Nothing to do
- break;
-
- case GT_PUTARG_STK:
- genPutArgStk(treeNode->AsPutArgStk());
- break;
-
- case GT_PUTARG_REG:
- genPutArgReg(treeNode->AsOp());
- break;
-
- case GT_CALL:
- genCallInstruction(treeNode->AsCall());
- break;
-
- case GT_JMP:
- genJmpMethod(treeNode);
- break;
-
- case GT_LOCKADD:
- case GT_XCHG:
- case GT_XADD:
- genLockedInstructions(treeNode->AsOp());
- break;
-
- case GT_MEMORYBARRIER:
- instGen_MemoryBarrier();
- break;
-
- case GT_CMPXCHG:
- NYI("GT_CMPXCHG");
- break;
-
- case GT_RELOAD:
- // do nothing - reload is just a marker.
- // The parent node will call genConsumeReg on this which will trigger the unspill of this node's child
- // into the register specified in this node.
- break;
-
- case GT_NOP:
- break;
-
- case GT_NO_OP:
- if (treeNode->gtFlags & GTF_NO_OP_NO)
- {
- noway_assert(!"GTF_NO_OP_NO should not be set");
- }
- else
- {
- instGen(INS_nop);
- }
- break;
-
- case GT_ARR_BOUNDS_CHECK:
-#ifdef FEATURE_SIMD
- case GT_SIMD_CHK:
-#endif // FEATURE_SIMD
- genRangeCheck(treeNode);
- break;
-
- case GT_PHYSREG:
- if (targetReg != treeNode->AsPhysReg()->gtSrcReg)
- {
- inst_RV_RV(ins_Copy(targetType), targetReg, treeNode->AsPhysReg()->gtSrcReg, targetType);
-
- genTransferRegGCState(targetReg, treeNode->AsPhysReg()->gtSrcReg);
- }
- genProduceReg(treeNode);
- break;
-
- case GT_PHYSREGDST:
- break;
-
- case GT_NULLCHECK:
- {
- assert(!treeNode->gtOp.gtOp1->isContained());
- regNumber reg = genConsumeReg(treeNode->gtOp.gtOp1);
- emit->emitIns_R_R_I(INS_ldr, EA_4BYTE, REG_ZR, reg, 0);
- }
- break;
-
- case GT_CATCH_ARG:
-
- noway_assert(handlerGetsXcptnObj(compiler->compCurBB->bbCatchTyp));
-
- /* Catch arguments get passed in a register. genCodeForBBlist()
- would have marked it as holding a GC object, but not used. */
-
- noway_assert(gcInfo.gcRegGCrefSetCur & RBM_EXCEPTION_OBJECT);
- genConsumeReg(treeNode);
- break;
-
- case GT_PINVOKE_PROLOG:
- noway_assert(((gcInfo.gcRegGCrefSetCur | gcInfo.gcRegByrefSetCur) & ~fullIntArgRegMask()) == 0);
-
- // the runtime side requires the codegen here to be consistent
- emit->emitDisableRandomNops();
- break;
-
- case GT_LABEL:
- genPendingCallLabel = genCreateTempLabel();
- treeNode->gtLabel.gtLabBB = genPendingCallLabel;
-
- // For long address (default): `adrp + add` will be emitted.
- // For short address (proven later): `adr` will be emitted.
- emit->emitIns_R_L(INS_adr, EA_PTRSIZE, genPendingCallLabel, targetReg);
- break;
-
- case GT_STORE_OBJ:
- if (treeNode->OperIsCopyBlkOp())
- {
- assert(treeNode->AsObj()->gtGcPtrCount != 0);
- genCodeForCpObj(treeNode->AsObj());
- break;
- }
- __fallthrough;
-
- case GT_STORE_DYN_BLK:
- case GT_STORE_BLK:
- {
- GenTreeBlk* blkOp = treeNode->AsBlk();
- if (blkOp->gtBlkOpGcUnsafe)
- {
- getEmitter()->emitDisableGC();
- }
- bool isCopyBlk = blkOp->OperIsCopyBlkOp();
-
- switch (blkOp->gtBlkOpKind)
- {
- case GenTreeBlk::BlkOpKindHelper:
- if (isCopyBlk)
- {
- genCodeForCpBlk(blkOp);
- }
- else
- {
- genCodeForInitBlk(blkOp);
- }
- break;
- case GenTreeBlk::BlkOpKindUnroll:
- if (isCopyBlk)
- {
- genCodeForCpBlkUnroll(blkOp);
- }
- else
- {
- genCodeForInitBlkUnroll(blkOp);
- }
- break;
- default:
- unreached();
- }
- if (blkOp->gtBlkOpGcUnsafe)
- {
- getEmitter()->emitEnableGC();
- }
- }
- break;
-
- case GT_JMPTABLE:
- genJumpTable(treeNode);
- break;
-
- case GT_SWITCH_TABLE:
- genTableBasedSwitch(treeNode);
- break;
-
- case GT_ARR_INDEX:
- genCodeForArrIndex(treeNode->AsArrIndex());
- break;
-
- case GT_ARR_OFFSET:
- genCodeForArrOffset(treeNode->AsArrOffs());
- break;
-
- case GT_CLS_VAR_ADDR:
- NYI("GT_CLS_VAR_ADDR");
- break;
-
- case GT_IL_OFFSET:
- // Do nothing; these nodes are simply markers for debug info.
- break;
-
- default:
- {
-#ifdef DEBUG
- char message[256];
- _snprintf_s(message, _countof(message), _TRUNCATE, "Unimplemented node type %s\n",
- GenTree::NodeName(treeNode->OperGet()));
-#endif
- assert(!"Unknown node in codegen");
- }
- break;
- }
-}
-
/***********************************************************************************************
* Generate code for localloc
*/
genProduceReg(tree);
}
+//------------------------------------------------------------------------
+// genCodeForDivMod: Produce code for a GT_DIV/GT_UDIV node. We don't see MOD:
+// (1) integer MOD is morphed into a sequence of sub, mul, div in fgMorph;
+// (2) float/double MOD is morphed into a helper call by front-end.
+//
+// Arguments:
+// tree - the node
+//
+void CodeGen::genCodeForDivMod(GenTreeOp* tree)
+{
+ assert(tree->OperIs(GT_DIV, GT_UDIV));
+
+ var_types targetType = tree->TypeGet();
+ emitter* emit = getEmitter();
+
+ genConsumeOperands(tree);
+
+ if (varTypeIsFloating(targetType))
+ {
+ // Floating point divide never raises an exception
+ genCodeForBinary(tree);
+ }
+ else // an integer divide operation
+ {
+ GenTreePtr divisorOp = tree->gtGetOp2();
+ emitAttr size = EA_ATTR(genTypeSize(genActualType(tree->TypeGet())));
+
+ if (divisorOp->IsIntegralConst(0))
+ {
+ // We unconditionally throw a divide by zero exception
+ genJumpToThrowHlpBlk(EJ_jmp, SCK_DIV_BY_ZERO);
+
+ // We still need to call genProduceReg
+ genProduceReg(tree);
+ }
+ else // the divisor is not the constant zero
+ {
+ regNumber divisorReg = divisorOp->gtRegNum;
+
+ // Generate the require runtime checks for GT_DIV or GT_UDIV
+ if (tree->gtOper == GT_DIV)
+ {
+ BasicBlock* sdivLabel = genCreateTempLabel();
+
+ // Two possible exceptions:
+ // (AnyVal / 0) => DivideByZeroException
+ // (MinInt / -1) => ArithmeticException
+ //
+ bool checkDividend = true;
+
+ // Do we have an immediate for the 'divisorOp'?
+ //
+ if (divisorOp->IsCnsIntOrI())
+ {
+ GenTreeIntConCommon* intConstTree = divisorOp->AsIntConCommon();
+ ssize_t intConstValue = intConstTree->IconValue();
+ assert(intConstValue != 0); // already checked above by IsIntegralConst(0))
+ if (intConstValue != -1)
+ {
+ checkDividend = false; // We statically know that the dividend is not -1
+ }
+ }
+ else // insert check for divison by zero
+ {
+ // Check if the divisor is zero throw a DivideByZeroException
+ emit->emitIns_R_I(INS_cmp, size, divisorReg, 0);
+ emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED);
+ genJumpToThrowHlpBlk(jmpEqual, SCK_DIV_BY_ZERO);
+ }
+
+ if (checkDividend)
+ {
+ // Check if the divisor is not -1 branch to 'sdivLabel'
+ emit->emitIns_R_I(INS_cmp, size, divisorReg, -1);
+
+ emitJumpKind jmpNotEqual = genJumpKindForOper(GT_NE, CK_SIGNED);
+ inst_JMP(jmpNotEqual, sdivLabel);
+ // If control flow continues past here the 'divisorReg' is known to be -1
+
+ regNumber dividendReg = tree->gtGetOp1()->gtRegNum;
+ // At this point the divisor is known to be -1
+ //
+ // Issue the 'adds zr, dividendReg, dividendReg' instruction
+ // this will set both the Z and V flags only when dividendReg is MinInt
+ //
+ emit->emitIns_R_R_R(INS_adds, size, REG_ZR, dividendReg, dividendReg);
+ inst_JMP(jmpNotEqual, sdivLabel); // goto sdiv if the Z flag is clear
+ genJumpToThrowHlpBlk(EJ_vs, SCK_ARITH_EXCPN); // if the V flags is set throw
+ // ArithmeticException
+
+ genDefineTempLabel(sdivLabel);
+ }
+ genCodeForBinary(tree); // Generate the sdiv instruction
+ }
+ else // (tree->gtOper == GT_UDIV)
+ {
+ // Only one possible exception
+ // (AnyVal / 0) => DivideByZeroException
+ //
+ // Note that division by the constant 0 was already checked for above by the
+ // op2->IsIntegralConst(0) check
+ //
+ if (!divisorOp->IsCnsIntOrI())
+ {
+ // divisorOp is not a constant, so it could be zero
+ //
+ emit->emitIns_R_I(INS_cmp, size, divisorReg, 0);
+ emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED);
+ genJumpToThrowHlpBlk(jmpEqual, SCK_DIV_BY_ZERO);
+ }
+ genCodeForBinary(tree);
+ }
+ }
+ }
+}
+
// Generate code for InitBlk by performing a loop unroll
// Preconditions:
// a) Both the size and fill byte value are integer constants.
#include "emit.h"
//------------------------------------------------------------------------
+// genCodeForTreeNode Generate code for a single node in the tree.
+//
+// Preconditions:
+// All operands have been evaluated.
+//
+void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
+{
+ regNumber targetReg = treeNode->gtRegNum;
+ var_types targetType = treeNode->TypeGet();
+ emitter* emit = getEmitter();
+
+#ifdef DEBUG
+ // Validate that all the operands for the current node are consumed in order.
+ // This is important because LSRA ensures that any necessary copies will be
+ // handled correctly.
+ lastConsumedNode = nullptr;
+ if (compiler->verbose)
+ {
+ unsigned seqNum = treeNode->gtSeqNum; // Useful for setting a conditional break in Visual Studio
+ compiler->gtDispLIRNode(treeNode, "Generating: ");
+ }
+#endif // DEBUG
+
+#ifdef _TARGET_ARM64_ // TODO-ARM: is this applicable to ARM32?
+ // Is this a node whose value is already in a register? LSRA denotes this by
+ // setting the GTF_REUSE_REG_VAL flag.
+ if (treeNode->IsReuseRegVal())
+ {
+ // For now, this is only used for constant nodes.
+ assert((treeNode->OperGet() == GT_CNS_INT) || (treeNode->OperGet() == GT_CNS_DBL));
+ JITDUMP(" TreeNode is marked ReuseReg\n");
+ return;
+ }
+#endif // _TARGET_ARM64_
+
+ // contained nodes are part of their parents for codegen purposes
+ // ex : immediates, most LEAs
+ if (treeNode->isContained())
+ {
+ return;
+ }
+
+ switch (treeNode->gtOper)
+ {
+#ifdef _TARGET_ARM64_
+
+ case GT_START_NONGC:
+ getEmitter()->emitDisableGC();
+ break;
+
+ case GT_PROF_HOOK:
+ // We should be seeing this only if profiler hook is needed
+ noway_assert(compiler->compIsProfilerHookNeeded());
+
+#ifdef PROFILING_SUPPORTED
+ // Right now this node is used only for tail calls. In future if
+ // we intend to use it for Enter or Leave hooks, add a data member
+ // to this node indicating the kind of profiler hook. For example,
+ // helper number can be used.
+ genProfilingLeaveCallback(CORINFO_HELP_PROF_FCN_TAILCALL);
+#endif // PROFILING_SUPPORTED
+ break;
+
+#endif // _TARGET_ARM64_
+
+ case GT_LCLHEAP:
+ genLclHeap(treeNode);
+ break;
+
+ case GT_CNS_INT:
+ case GT_CNS_DBL:
+ genSetRegToConst(targetReg, targetType, treeNode);
+ genProduceReg(treeNode);
+ break;
+
+ case GT_NOT:
+ case GT_NEG:
+ genCodeForNegNot(treeNode);
+ break;
+
+ case GT_MOD:
+ case GT_UMOD:
+ case GT_DIV:
+ case GT_UDIV:
+ genCodeForDivMod(treeNode->AsOp());
+ break;
+
+ case GT_OR:
+ case GT_XOR:
+ case GT_AND:
+ assert(varTypeIsIntegralOrI(treeNode));
+
+ __fallthrough;
+
+#ifdef _TARGET_ARM_
+ case GT_ADD_LO:
+ case GT_ADD_HI:
+ case GT_SUB_LO:
+ case GT_SUB_HI:
+#endif // _TARGET_ARM_
+
+ case GT_ADD:
+ case GT_SUB:
+ case GT_MUL:
+ genConsumeOperands(treeNode->AsOp());
+ genCodeForBinary(treeNode);
+ break;
+
+ case GT_LSH:
+ case GT_RSH:
+ case GT_RSZ:
+ case GT_ROR:
+ genCodeForShift(treeNode);
+ break;
+
+#ifdef _TARGET_ARM_
+
+ case GT_LSH_HI:
+ case GT_RSH_LO:
+ genCodeForShiftLong(treeNode);
+ break;
+
+#endif // _TARGET_ARM_
+
+ case GT_CAST:
+ genCodeForCast(treeNode->AsOp());
+ break;
+
+ case GT_LCL_FLD_ADDR:
+ case GT_LCL_VAR_ADDR:
+ genCodeForLclAddr(treeNode);
+ break;
+
+ case GT_LCL_FLD:
+ genCodeForLclFld(treeNode->AsLclFld());
+ break;
+
+ case GT_LCL_VAR:
+ genCodeForLclVar(treeNode->AsLclVar());
+ break;
+
+ case GT_STORE_LCL_FLD:
+ genCodeForStoreLclFld(treeNode->AsLclFld());
+ break;
+
+ case GT_STORE_LCL_VAR:
+ genCodeForStoreLclVar(treeNode->AsLclVar());
+ break;
+
+ case GT_RETFILT:
+ case GT_RETURN:
+ genReturn(treeNode);
+ break;
+
+ case GT_LEA:
+ // if we are here, it is the case where there is an LEA that cannot
+ // be folded into a parent instruction
+ genLeaInstruction(treeNode->AsAddrMode());
+ break;
+
+ case GT_IND:
+ genCodeForIndir(treeNode->AsIndir());
+ break;
+
+#ifdef _TARGET_ARM64_
+
+ case GT_MULHI:
+ genCodeForMulHi(treeNode->AsOp());
+ break;
+
+ case GT_CKFINITE:
+ genCkfinite(treeNode);
+ break;
+
+ case GT_SWAP:
+ genCodeForSwap(treeNode->AsOp());
+ break;
+
+ case GT_JMP:
+ genJmpMethod(treeNode);
+ break;
+
+#endif // _TARGET_ARM64_
+
+ case GT_INTRINSIC:
+ genIntrinsic(treeNode);
+ break;
+
+#ifdef FEATURE_SIMD
+ case GT_SIMD:
+ genSIMDIntrinsic(treeNode->AsSIMD());
+ break;
+#endif // FEATURE_SIMD
+
+ case GT_EQ:
+ case GT_NE:
+ case GT_LT:
+ case GT_LE:
+ case GT_GE:
+ case GT_GT:
+ genCodeForCompare(treeNode->AsOp());
+ break;
+
+ case GT_JTRUE:
+ genCodeForJumpTrue(treeNode);
+ break;
+
+#ifdef _TARGET_ARM_
+
+ case GT_JCC:
+ genCodeForJcc(treeNode->AsJumpCC());
+ break;
+
+#endif // _TARGET_ARM_
+
+ case GT_RETURNTRAP:
+ genCodeForReturnTrap(treeNode->AsOp());
+ break;
+
+ case GT_STOREIND:
+ genCodeForStoreInd(treeNode->AsStoreInd());
+ break;
+
+ case GT_COPY:
+ // This is handled at the time we call genConsumeReg() on the GT_COPY
+ break;
+
+ case GT_LIST:
+ case GT_FIELD_LIST:
+ case GT_ARGPLACE:
+ // Nothing to do
+ break;
+
+ case GT_PUTARG_STK:
+ genPutArgStk(treeNode->AsPutArgStk());
+ break;
+
+ case GT_PUTARG_REG:
+ genPutArgReg(treeNode->AsOp());
+ break;
+
+ case GT_CALL:
+ genCallInstruction(treeNode->AsCall());
+ break;
+
+ case GT_LOCKADD:
+ case GT_XCHG:
+ case GT_XADD:
+ genLockedInstructions(treeNode->AsOp());
+ break;
+
+ case GT_MEMORYBARRIER:
+ instGen_MemoryBarrier();
+ break;
+
+ case GT_CMPXCHG:
+ NYI("GT_CMPXCHG");
+ break;
+
+ case GT_RELOAD:
+ // do nothing - reload is just a marker.
+ // The parent node will call genConsumeReg on this which will trigger the unspill of this node's child
+ // into the register specified in this node.
+ break;
+
+ case GT_NOP:
+ break;
+
+ case GT_NO_OP:
+ if (treeNode->gtFlags & GTF_NO_OP_NO)
+ {
+ noway_assert(!"GTF_NO_OP_NO should not be set");
+ }
+ else
+ {
+ instGen(INS_nop);
+ }
+ break;
+
+ case GT_ARR_BOUNDS_CHECK:
+#ifdef FEATURE_SIMD
+ case GT_SIMD_CHK:
+#endif // FEATURE_SIMD
+ genRangeCheck(treeNode);
+ break;
+
+ case GT_PHYSREG:
+ genCodeForPhysReg(treeNode->AsPhysReg());
+ break;
+
+ case GT_PHYSREGDST:
+ break;
+
+ case GT_NULLCHECK:
+ genCodeForNullCheck(treeNode->AsOp());
+ break;
+
+ case GT_CATCH_ARG:
+
+ noway_assert(handlerGetsXcptnObj(compiler->compCurBB->bbCatchTyp));
+
+ /* Catch arguments get passed in a register. genCodeForBBlist()
+ would have marked it as holding a GC object, but not used. */
+
+ noway_assert(gcInfo.gcRegGCrefSetCur & RBM_EXCEPTION_OBJECT);
+ genConsumeReg(treeNode);
+ break;
+
+ case GT_PINVOKE_PROLOG:
+ noway_assert(((gcInfo.gcRegGCrefSetCur | gcInfo.gcRegByrefSetCur) & ~fullIntArgRegMask()) == 0);
+
+ // the runtime side requires the codegen here to be consistent
+ emit->emitDisableRandomNops();
+ break;
+
+ case GT_LABEL:
+ genPendingCallLabel = genCreateTempLabel();
+ treeNode->gtLabel.gtLabBB = genPendingCallLabel;
+ emit->emitIns_R_L(INS_adr, EA_PTRSIZE, genPendingCallLabel, targetReg);
+ break;
+
+ case GT_STORE_OBJ:
+ case GT_STORE_DYN_BLK:
+ case GT_STORE_BLK:
+ genCodeForStoreBlk(treeNode->AsBlk());
+ break;
+
+ case GT_JMPTABLE:
+ genJumpTable(treeNode);
+ break;
+
+ case GT_SWITCH_TABLE:
+ genTableBasedSwitch(treeNode);
+ break;
+
+ case GT_ARR_INDEX:
+ genCodeForArrIndex(treeNode->AsArrIndex());
+ break;
+
+ case GT_ARR_OFFSET:
+ genCodeForArrOffset(treeNode->AsArrOffs());
+ break;
+
+#ifdef _TARGET_ARM_
+
+ case GT_CLS_VAR_ADDR:
+ emit->emitIns_R_C(INS_lea, EA_PTRSIZE, targetReg, treeNode->gtClsVar.gtClsVarHnd, 0);
+ genProduceReg(treeNode);
+ break;
+
+#endif // _TARGET_ARM_
+
+ case GT_IL_OFFSET:
+ // Do nothing; these nodes are simply markers for debug info.
+ break;
+
+ default:
+ {
+#ifdef DEBUG
+ char message[256];
+ _snprintf_s(message, _countof(message), _TRUNCATE, "NYI: Unimplemented node type %s",
+ GenTree::NodeName(treeNode->OperGet()));
+ NYIRAW(message);
+#else
+ NYI("unimplemented node");
+#endif
+ }
+ break;
+ }
+}
+
+//------------------------------------------------------------------------
// genSetRegToIcon: Generate code that will set the given register to the integer constant.
//
void CodeGen::genSetRegToIcon(regNumber reg, ssize_t val, var_types type, insFlags flags)
//
void CodeGen::genIntrinsic(GenTreePtr treeNode)
{
+ assert(treeNode->OperIs(GT_INTRINSIC));
+
// Both operand and its result must be of the same floating point type.
GenTreePtr srcNode = treeNode->gtOp.gtOp1;
assert(varTypeIsFloating(srcNode));
// genPutArgReg - generate code for a GT_PUTARG_REG node
//
// Arguments
-// treeNode - the GT_PUTARG_REG node
+// tree - the GT_PUTARG_REG node
//
// Return value:
// None
genJumpToThrowHlpBlk(jmpKind, SCK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
}
+//---------------------------------------------------------------------
+// genCodeForPhysReg - generate code for a GT_PHYSREG node
+//
+// Arguments
+// tree - the GT_PHYSREG node
+//
+// Return value:
+// None
+//
+void CodeGen::genCodeForPhysReg(GenTreePhysReg* tree)
+{
+ assert(tree->OperIs(GT_PHYSREG));
+ var_types targetType = tree->TypeGet();
+ regNumber targetReg = tree->gtRegNum;
+
+ if (targetReg != tree->gtSrcReg)
+ {
+ inst_RV_RV(ins_Copy(targetType), targetReg, tree->gtSrcReg, targetType);
+ genTransferRegGCState(targetReg, tree->gtSrcReg);
+ }
+
+ genProduceReg(tree);
+}
+
+//---------------------------------------------------------------------
+// genCodeForNullCheck - generate code for a GT_NULLCHECK node
+//
+// Arguments
+// tree - the GT_NULLCHECK node
+//
+// Return value:
+// None
+//
+void CodeGen::genCodeForNullCheck(GenTreeOp* tree)
+{
+ assert(tree->OperIs(GT_NULLCHECK));
+ assert(!tree->gtOp1->isContained());
+ regNumber addrReg = genConsumeReg(tree->gtOp1);
+
+#ifdef _TARGET_ARM64_
+ regNumber targetReg = REG_ZR;
+#else
+ regNumber targetReg = tree->gtRegNum;
+#endif
+
+ getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, targetReg, addrReg, 0);
+}
+
//------------------------------------------------------------------------
// genOffsetOfMDArrayLowerBound: Returns the offset from the Array object to the
// lower bound for the given dimension.
}
//------------------------------------------------------------------------
+// genCodeForCast: Generates the code for GT_CAST.
+//
+// Arguments:
+// tree - the GT_CAST node.
+//
+void CodeGen::genCodeForCast(GenTreeOp* tree)
+{
+ assert(tree->OperIs(GT_CAST));
+
+ var_types targetType = tree->TypeGet();
+ regNumber targetReg = tree->gtRegNum;
+
+ // Cast is never contained (?)
+ noway_assert(targetReg != REG_NA);
+
+ if (varTypeIsFloating(targetType) && varTypeIsFloating(tree->gtOp1))
+ {
+ // Casts float/double <--> double/float
+ genFloatToFloatCast(tree);
+ }
+ else if (varTypeIsFloating(tree->gtOp1))
+ {
+ // Casts float/double --> int32/int64
+ genFloatToIntCast(tree);
+ }
+ else if (varTypeIsFloating(targetType))
+ {
+ // Casts int32/uint32/int64/uint64 --> float/double
+ genIntToFloatCast(tree);
+ }
+ else
+ {
+ // Casts int <--> int
+ genIntToIntCast(tree);
+ }
+ // The per-case functions call genProduceReg()
+}
+
+//------------------------------------------------------------------------
+// genCodeForLclAddr: Generates the code for GT_LCL_FLD_ADDR/GT_LCL_VAR_ADDR.
+//
+// Arguments:
+// tree - the node.
+//
+void CodeGen::genCodeForLclAddr(GenTree* tree)
+{
+ assert(tree->OperIs(GT_LCL_FLD_ADDR, GT_LCL_VAR_ADDR));
+
+ var_types targetType = tree->TypeGet();
+ regNumber targetReg = tree->gtRegNum;
+
+ // Address of a local var. This by itself should never be allocated a register.
+ // If it is worth storing the address in a register then it should be cse'ed into
+ // a temp and that would be allocated a register.
+ noway_assert(targetType == TYP_BYREF);
+ noway_assert(!tree->InReg());
+
+ inst_RV_TT(INS_lea, targetReg, tree, 0, EA_BYREF);
+ genProduceReg(tree);
+}
+
+//------------------------------------------------------------------------
// genCodeForLclFld: Produce code for a GT_LCL_FLD node.
//
// Arguments:
//
void CodeGen::genCodeForLclFld(GenTreeLclFld* tree)
{
+ assert(tree->OperIs(GT_LCL_FLD));
+
var_types targetType = tree->TypeGet();
regNumber targetReg = tree->gtRegNum;
emitter* emit = getEmitter();
genProduceReg(tree);
}
+//------------------------------------------------------------------------
+// genCodeForIndir: Produce code for a GT_IND node.
+//
+// Arguments:
+// tree - the GT_IND node
+//
+void CodeGen::genCodeForIndir(GenTreeIndir* tree)
+{
+ assert(tree->OperIs(GT_IND));
+
+ var_types targetType = tree->TypeGet();
+ regNumber targetReg = tree->gtRegNum;
+ emitter* emit = getEmitter();
+
+ genConsumeAddress(tree->Addr());
+ emit->emitInsLoadStoreOp(ins_Load(targetType), emitTypeSize(tree), targetReg, tree);
+ genProduceReg(tree);
+}
+
// Generate code for a CpBlk node by the means of the VM memcpy helper call
// Preconditions:
// a) The size argument of the CpBlk is not an integer constant
}
}
+//------------------------------------------------------------------------
+// genCodeForStoreBlk: Produce code for a GT_STORE_OBJ/GT_STORE_DYN_BLK/GT_STORE_BLK node.
+//
+// Arguments:
+// tree - the node
+//
+void CodeGen::genCodeForStoreBlk(GenTreeBlk* blkOp)
+{
+ assert(blkOp->OperIs(GT_STORE_OBJ, GT_STORE_DYN_BLK, GT_STORE_BLK));
+
+#ifdef _TARGET_ARM_
+ NYI_IF(blkOp->OperIs(GT_STORE_OBJ), "GT_STORE_OBJ");
+#endif // _TARGET_ARM_
+
+#ifndef _TARGET_ARM_ // NYI for ARM
+ if (blkOp->OperIs(GT_STORE_OBJ) && blkOp->OperIsCopyBlkOp())
+ {
+ assert(blkOp->AsObj()->gtGcPtrCount != 0);
+ genCodeForCpObj(blkOp->AsObj());
+ return;
+ }
+#endif // !_TARGET_ARM_
+
+ if (blkOp->gtBlkOpGcUnsafe)
+ {
+ getEmitter()->emitDisableGC();
+ }
+ bool isCopyBlk = blkOp->OperIsCopyBlkOp();
+
+ switch (blkOp->gtBlkOpKind)
+ {
+ case GenTreeBlk::BlkOpKindHelper:
+ if (isCopyBlk)
+ {
+ genCodeForCpBlk(blkOp);
+ }
+ else
+ {
+ genCodeForInitBlk(blkOp);
+ }
+ break;
+
+ case GenTreeBlk::BlkOpKindUnroll:
+ if (isCopyBlk)
+ {
+ genCodeForCpBlkUnroll(blkOp);
+ }
+ else
+ {
+ genCodeForInitBlkUnroll(blkOp);
+ }
+ break;
+
+ default:
+ unreached();
+ }
+
+ if (blkOp->gtBlkOpGcUnsafe)
+ {
+ getEmitter()->emitEnableGC();
+ }
+}
+
#endif // _TARGET_ARMARCH_
#endif // !LEGACY_BACKEND
#ifndef LEGACY_BACKEND // Not necessary (it's this way in the #include location), but helpful to IntelliSense
void genSetRegToConst(regNumber targetReg, var_types targetType, GenTreePtr tree);
-
void genCodeForTreeNode(GenTreePtr treeNode);
-
void genCodeForBinary(GenTreePtr treeNode);
#if defined(_TARGET_X86_)
#endif // _TARGET_X86_
void genCodeForDivMod(GenTreeOp* treeNode);
-
void genCodeForMulHi(GenTreeOp* treeNode);
-
void genLeaInstruction(GenTreeAddrMode* lea);
-
void genSetRegToCond(regNumber dstReg, GenTreePtr tree);
#if !defined(_TARGET_64BIT_)
#endif
void genIntToIntCast(GenTreePtr treeNode);
-
void genFloatToFloatCast(GenTreePtr treeNode);
-
void genFloatToIntCast(GenTreePtr treeNode);
-
void genIntToFloatCast(GenTreePtr treeNode);
-
void genCkfinite(GenTreePtr treeNode);
-
void genCodeForCompare(GenTreeOp* tree);
-
void genIntrinsic(GenTreePtr treeNode);
-
void genPutArgStk(GenTreePutArgStk* treeNode);
-
void genPutArgReg(GenTreeOp* tree);
#if defined(_TARGET_XARCH_)
#endif // _TARGET_XARCH_ || _TARGET_ARM64_
void genCompareFloat(GenTreePtr treeNode);
-
void genCompareInt(GenTreePtr treeNode);
#if !defined(_TARGET_64BIT_)
void genSIMDIntrinsicShuffleSSE2(GenTreeSIMD* simdNode);
void genSIMDIntrinsicUpperSave(GenTreeSIMD* simdNode);
void genSIMDIntrinsicUpperRestore(GenTreeSIMD* simdNode);
-
void genSIMDIntrinsic(GenTreeSIMD* simdNode);
void genSIMDCheck(GenTree* treeNode);
#endif // !defined(_TARGET_64BIT_)
void genProduceReg(GenTree* tree);
-
void genUnspillRegIfNeeded(GenTree* tree);
-
regNumber genConsumeReg(GenTree* tree);
-
void genCopyRegIfNeeded(GenTree* tree, regNumber needReg);
void genConsumeRegAndCopy(GenTree* tree, regNumber needReg);
}
void genRegCopy(GenTreePtr tree);
-
void genTransferRegGCState(regNumber dst, regNumber src);
-
void genConsumeAddress(GenTree* addr);
-
void genConsumeAddrMode(GenTreeAddrMode* mode);
-
void genSetBlockSize(GenTreeBlk* blkNode, regNumber sizeReg);
void genConsumeBlockSrc(GenTreeBlk* blkNode);
void genSetBlockSrc(GenTreeBlk* blkNode, regNumber srcReg);
#endif // FEATURE_PUT_STRUCT_ARG_STK
void genConsumeRegs(GenTree* tree);
-
void genConsumeOperands(GenTreeOp* tree);
-
void genEmitGSCookieCheck(bool pushReg);
-
void genSetRegToIcon(regNumber reg, ssize_t val, var_types type = TYP_INT, insFlags flags = INS_FLAGS_DONT_CARE);
-
void genCodeForShift(GenTreePtr tree);
#if defined(_TARGET_X86_) || defined(_TARGET_ARM_)
void genCodeForShiftRMW(GenTreeStoreInd* storeInd);
#endif // _TARGET_XARCH_
+void genCodeForCast(GenTreeOp* tree);
+void genCodeForLclAddr(GenTree* tree);
+void genCodeForIndir(GenTreeIndir* tree);
void genCodeForNegNot(GenTree* tree);
-
void genCodeForLclVar(GenTreeLclVar* tree);
-
void genCodeForLclFld(GenTreeLclFld* tree);
-
void genCodeForStoreLclFld(GenTreeLclFld* tree);
-
void genCodeForStoreLclVar(GenTreeLclVar* tree);
-
void genCodeForReturnTrap(GenTreeOp* tree);
-
+void genCodeForJcc(GenTreeJumpCC* tree);
void genCodeForStoreInd(GenTreeStoreInd* tree);
-
void genCodeForSwap(GenTreeOp* tree);
-
void genCodeForCpObj(GenTreeObj* cpObjNode);
-
void genCodeForCpBlk(GenTreeBlk* cpBlkNode);
-
void genCodeForCpBlkRepMovs(GenTreeBlk* cpBlkNode);
-
void genCodeForCpBlkUnroll(GenTreeBlk* cpBlkNode);
+void genCodeForPhysReg(GenTreePhysReg* tree);
+void genCodeForNullCheck(GenTreeOp* tree);
void genAlignStackBeforeCall(GenTreePutArgStk* putArgStk);
void genAlignStackBeforeCall(GenTreeCall* call);
#endif // FEATURE_PUT_STRUCT_ARG_STK
void genCodeForLoadOffset(instruction ins, emitAttr size, regNumber dst, GenTree* base, unsigned offset);
-
void genCodeForStoreOffset(instruction ins, emitAttr size, regNumber src, GenTree* base, unsigned offset);
#ifdef _TARGET_ARM64_
void genCodeForLoadPairOffset(regNumber dst, regNumber dst2, GenTree* base, unsigned offset);
-
void genCodeForStorePairOffset(regNumber src, regNumber src2, GenTree* base, unsigned offset);
#endif // _TARGET_ARM64_
void genCodeForStoreBlk(GenTreeBlk* storeBlkNode);
-
void genCodeForInitBlk(GenTreeBlk* initBlkNode);
-
void genCodeForInitBlkRepStos(GenTreeBlk* initBlkNode);
-
void genCodeForInitBlkUnroll(GenTreeBlk* initBlkNode);
-
void genJumpTable(GenTree* tree);
-
void genTableBasedSwitch(GenTree* tree);
-
void genCodeForArrIndex(GenTreeArrIndex* treeNode);
-
void genCodeForArrOffset(GenTreeArrOffs* treeNode);
-
instruction genGetInsForOper(genTreeOps oper, var_types type);
-
void genStoreInd(GenTreePtr node);
-
bool genEmitOptimizedGCWriteBarrier(GCInfo::WriteBarrierForm writeBarrierForm, GenTree* addr, GenTree* data);
-
void genCallInstruction(GenTreeCall* call);
-
void genJmpMethod(GenTreePtr jmp);
-
BasicBlock* genCallFinally(BasicBlock* block);
-
void genCodeForJumpTrue(GenTreePtr tree);
#if FEATURE_EH_FUNCLETS
bool isStructReturn(GenTreePtr treeNode);
void genStructReturn(GenTreePtr treeNode);
-// Codegen for GT_RETURN.
void genReturn(GenTreePtr treeNode);
void genLclHeap(GenTreePtr tree);