}
}
-// generate code for a DIV or MOD operation
+//------------------------------------------------------------------------
+// genCodeForDivMod: Generate code for a DIV or MOD operation.
+//
+// Arguments:
+// treeNode - the node to generate the code for
//
void CodeGen::genCodeForDivMod(GenTreeOp* treeNode)
{
var_types targetType = treeNode->TypeGet();
emitter* emit = getEmitter();
- // dividend is not contained.
- assert(!dividend->isContained());
+#ifdef _TARGET_X86_
+ bool dividendIsLong = varTypeIsLong(dividend->TypeGet());
+ GenTree* dividendLo;
+ GenTree* dividendHi;
+
+ if (dividendIsLong)
+ {
+ // If dividend is a GT_LONG, the we need to make sure its lo and hi parts are not contained.
+ dividendLo = dividend->gtGetOp1();
+ dividendHi = dividend->gtGetOp2();
+
+ assert(!dividendLo->isContained());
+ assert(!dividendHi->isContained());
+ assert(divisor->IsCnsIntOrI());
+ }
+ else
+#endif
+ {
+ // dividend is not contained.
+ assert(!dividend->isContained());
+ }
genConsumeOperands(treeNode->AsOp());
if (varTypeIsFloating(targetType))
}
else
{
- // dividend must be in RAX
+#ifdef _TARGET_X86_
+ if (dividendIsLong)
+ {
+ assert(dividendLo != nullptr && dividendHi != nullptr);
+
+ // dividendLo must be in RAX; dividendHi must be in RDX
+ if (dividendLo->gtRegNum != REG_EAX)
+ {
+ inst_RV_RV(INS_mov, REG_EAX, dividendLo->gtRegNum, targetType);
+ }
+ if (dividendHi->gtRegNum != REG_EDX)
+ {
+ inst_RV_RV(INS_mov, REG_EDX, dividendHi->gtRegNum, targetType);
+ }
+ }
+ else
+#endif
if (dividend->gtRegNum != REG_RAX)
{
+ // dividend must be in RAX
inst_RV_RV(INS_mov, REG_RAX, dividend->gtRegNum, targetType);
}
// zero or sign extend rax to rdx
- if (oper == GT_UMOD || oper == GT_UDIV)
- {
- instGen_Set_Reg_To_Zero(EA_PTRSIZE, REG_EDX);
- }
- else
+#ifdef _TARGET_X86_
+ if (!dividendIsLong)
+#endif
{
- emit->emitIns(INS_cdq, size);
- // the cdq instruction writes RDX, So clear the gcInfo for RDX
- gcInfo.gcMarkRegSetNpt(RBM_RDX);
+ if (oper == GT_UMOD || oper == GT_UDIV)
+ {
+ instGen_Set_Reg_To_Zero(EA_PTRSIZE, REG_EDX);
+ }
+ else
+ {
+ emit->emitIns(INS_cdq, size);
+ // the cdq instruction writes RDX, So clear the gcInfo for RDX
+ gcInfo.gcMarkRegSetNpt(RBM_RDX);
+ }
}
// Perform the 'targetType' (64-bit or 32-bit) divide instruction
break;
case GT_UMOD:
- NYI("Arithmetic binary operators on TYP_LONG - GT_UMOD");
+ nextNode = DecomposeUMod(use);
break;
case GT_LSH:
}
//------------------------------------------------------------------------
+// DecomposeUMod: Decompose GT_UMOD. The only GT_UMODs that make it to decompose
+// are guaranteed to be an unsigned long mod with op2 which is a cast to long from
+// a constant int whose value is between 2 and 0x3fffffff. All other GT_UMODs are
+// morphed into helper calls. These GT_UMODs will actually return an int value in
+// RDX. In decompose, we make the lo operation a TYP_INT GT_UMOD, with op2 as the
+// original lo half and op1 as a GT_LONG. We make the hi part 0, so we end up with:
+//
+// GT_UMOD[TYP_INT] ( GT_LONG [TYP_LONG] (loOp1, hiOp1), loOp2 [TYP_INT] )
+//
+// With the expectation that we will generate:
+//
+// EDX = hiOp1
+// EAX = loOp1
+// reg = loOp2
+// idiv reg
+// EDX is the remainder, and result of GT_UMOD
+// mov hiReg = 0
+//
+// Arguments:
+// use - the LIR::Use object for the def that needs to be decomposed.
+//
+// Return Value:
+// The next node to process.
+//
+GenTree* DecomposeLongs::DecomposeUMod(LIR::Use& use)
+{
+ assert(use.IsInitialized());
+
+ GenTree* tree = use.Def();
+ genTreeOps oper = tree->OperGet();
+
+ assert(oper == GT_UMOD);
+
+ GenTree* op1 = tree->gtGetOp1();
+ GenTree* op2 = tree->gtGetOp2();
+ assert(op1->OperGet() == GT_LONG);
+ assert(op2->OperGet() == GT_LONG);
+
+ GenTree* loOp2 = op2->gtGetOp1();
+ GenTree* hiOp2 = op2->gtGetOp2();
+
+ assert(loOp2->OperGet() == GT_CNS_INT);
+ assert(hiOp2->OperGet() == GT_CNS_INT);
+ assert((loOp2->gtIntCon.gtIconVal >= 2) && (loOp2->gtIntCon.gtIconVal <= 0x3fffffff));
+ assert(hiOp2->gtIntCon.gtIconVal == 0);
+
+ // Get rid of op2's hi part. We don't need it.
+ Range().Remove(hiOp2);
+ Range().Remove(op2);
+
+ // Lo part is the GT_UMOD
+ GenTree* loResult = tree;
+ loResult->gtOp.gtOp2 = loOp2;
+ loResult->gtType = TYP_INT;
+
+ // Set the high part to 0
+ GenTree* hiResult = m_compiler->gtNewZeroConNode(TYP_INT);
+
+ Range().InsertAfter(loResult, hiResult);
+
+ return FinalizeDecomposition(use, loResult, hiResult);
+}
+
+//------------------------------------------------------------------------
// StoreNodeToVar: Check if the user is a STORE_LCL_VAR, and if it isn't,
// store the node to a var. Then decompose the new LclVar.
//
GenTree* DecomposeArith(LIR::Use& use);
GenTree* DecomposeShift(LIR::Use& use);
GenTree* DecomposeMul(LIR::Use& use);
+ GenTree* DecomposeUMod(LIR::Use& use);
// Helper functions
GenTree* FinalizeDecomposition(LIR::Use& use, GenTree* loResult, GenTree* hiResult);
assert((node->OperGet() == GT_UDIV) || (node->OperGet() == GT_UMOD));
GenTree* divisor = node->gtGetOp2();
+ GenTree* dividend = node->gtGetOp1();
- if (divisor->IsCnsIntOrI())
+ if (divisor->IsCnsIntOrI()
+#ifdef _TARGET_X86_
+ && (dividend->OperGet() != GT_LONG)
+#endif
+ )
{
size_t divisorValue = static_cast<size_t>(divisor->gtIntCon.IconValue());
info->setDstCandidates(l, RBM_RAX);
}
- // If possible would like to have op1 in RAX to avoid a register move
- op1->gtLsraInfo.setSrcCandidates(l, RBM_RAX);
+#ifdef _TARGET_X86_
+ if (op1->OperGet() == GT_LONG)
+ {
+ // To avoid reg move would like to have op1's low part in RAX and high part in RDX.
+ GenTree* loVal = op1->gtGetOp1();
+ GenTree* hiVal = op1->gtGetOp2();
+
+ // Src count is actually 3, so increment.
+ assert(op2->IsCnsIntOrI());
+ info->srcCount++;
+
+ loVal->gtLsraInfo.setSrcCandidates(l, RBM_EAX);
+ hiVal->gtLsraInfo.setSrcCandidates(l, RBM_EDX);
+ }
+ else
+#endif
+ {
+ // If possible would like to have op1 in RAX to avoid a register move
+ op1->gtLsraInfo.setSrcCandidates(l, RBM_RAX);
+ }
// divisor can be an r/m, but the memory indirection must be of the same size as the divide
if (op2->isMemoryOp() && (op2->TypeGet() == tree->TypeGet()))