diff --git a/src/jit/codegenarm64.cpp b/src/jit/codegenarm64.cpp index b690695f78c5..4f15fe3b977a 100644 --- a/src/jit/codegenarm64.cpp +++ b/src/jit/codegenarm64.cpp @@ -3365,118 +3365,6 @@ CodeGen::genCodeForTreeNode(GenTreePtr treeNode) } } - -// Generate code for division (or mod) by power of two -// or negative powers of two. (meaning -1 * a power of two, not 2^(-1)) -// Op2 must be a contained integer constant. -void -CodeGen::genCodeForPow2Div(GenTreeOp* tree) -{ -#if 0 - GenTree *dividend = tree->gtOp.gtOp1; - GenTree *divisor = tree->gtOp.gtOp2; - genTreeOps oper = tree->OperGet(); - emitAttr size = emitTypeSize(tree); - emitter *emit = getEmitter(); - regNumber targetReg = tree->gtRegNum; - var_types targetType = tree->TypeGet(); - - bool isSigned = oper == GT_MOD || oper == GT_DIV; - - // precondition: extended dividend is in RDX:RAX - // which means it is either all zeros or all ones - - noway_assert(divisor->isContained()); - GenTreeIntConCommon* divImm = divisor->AsIntConCommon(); - int64_t imm = divImm->IconValue(); - ssize_t abs_imm = abs(imm); - noway_assert(isPow2(abs_imm)); - - - if (isSigned) - { - if (imm == 1) - { - if (targetReg != REG_RAX) - inst_RV_RV(INS_mov, targetReg, REG_RAX, targetType); - - return; - } - - if (abs_imm == 2) - { - if (oper == GT_MOD) - { - emit->emitIns_R_I(INS_and, size, REG_RAX, 1); // result is 0 or 1 - // xor with rdx will flip all bits if negative - emit->emitIns_R_R(INS_xor, size, REG_RAX, REG_RDX); // 111.11110 or 0 - } - else - { - assert(oper == GT_DIV); - // add 1 if it's negative - emit->emitIns_R_R(INS_sub, size, REG_RAX, REG_RDX); - } - } - else - { - // add imm-1 if negative - emit->emitIns_R_I(INS_and, size, REG_RDX, abs_imm - 1); - emit->emitIns_R_R(INS_add, size, REG_RAX, REG_RDX); - } - - if (oper == GT_DIV) - { - unsigned shiftAmount = genLog2(unsigned(abs_imm)); - inst_RV_SH(INS_sar, size, REG_RAX, shiftAmount); - - if (imm < 0) - { - emit->emitIns_R(INS_neg, size, REG_RAX); - } - } - else - { - assert(oper == GT_MOD); - if (abs_imm > 2) - { - emit->emitIns_R_I(INS_and, size, REG_RAX, abs_imm - 1); - } - // RDX contains 'imm-1' if negative - emit->emitIns_R_R(INS_sub, size, REG_RAX, REG_RDX); - } - - if (targetReg != REG_RAX) - { - inst_RV_RV(INS_mov, targetReg, REG_RAX, targetType); - } - } - else - { - assert (imm > 0); - - if (targetReg != dividend->gtRegNum) - { - inst_RV_RV(INS_mov, targetReg, dividend->gtRegNum, targetType); - } - - if (oper == GT_UDIV) - { - inst_RV_SH(INS_shr, size, targetReg, genLog2(unsigned(imm))); - } - else - { - assert(oper == GT_UMOD); - - emit->emitIns_R_I(INS_and, size, targetReg, imm -1); - } - } -#else // !0 - NYI("genCodeForPow2Div"); -#endif // !0 -} - - /*********************************************************************************************** * Generate code for localloc */ diff --git a/src/jit/codegenlinear.h b/src/jit/codegenlinear.h index 3c96fbec07a4..6ffccbf59322 100644 --- a/src/jit/codegenlinear.h +++ b/src/jit/codegenlinear.h @@ -20,8 +20,6 @@ void genCodeForMulHi(GenTreeOp* treeNode); - void genCodeForPow2Div(GenTreeOp* treeNode); - void genLeaInstruction(GenTreeAddrMode *lea); void genSetRegToCond(regNumber dstReg, GenTreePtr tree); diff --git a/src/jit/codegenxarch.cpp b/src/jit/codegenxarch.cpp index a2bdb3802adc..84803f07baf0 100755 --- a/src/jit/codegenxarch.cpp +++ b/src/jit/codegenxarch.cpp @@ -1280,42 +1280,30 @@ void CodeGen::genCodeForDivMod(GenTreeOp* treeNode) gcInfo.gcMarkRegSetNpt(RBM_RDX); } - if (divisor->isContainedIntOrIImmed()) - { - GenTreeIntConCommon* divImm = divisor->AsIntConCommon(); - assert(divImm->IsIntCnsFitsInI32()); - ssize_t imm = divImm->IconValue(); - assert(isPow2(abs(imm))); - genCodeForPow2Div(treeNode->AsOp()); - } + // Perform the 'targetType' (64-bit or 32-bit) divide instruction + instruction ins; + if (oper == GT_UMOD || oper == GT_UDIV) + ins = INS_div; else - { - // Perform the 'targetType' (64-bit or 32-bit) divide instruction - instruction ins; - if (oper == GT_UMOD || oper == GT_UDIV) - ins = INS_div; - else - ins = INS_idiv; + ins = INS_idiv; - emit->emitInsBinary(ins, size, treeNode, divisor); + emit->emitInsBinary(ins, size, treeNode, divisor); - // Signed divide RDX:RAX by r/m64, with result - // stored in RAX := Quotient, RDX := Remainder. - // Move the result to the desired register, if necessary - if (oper == GT_DIV || oper == GT_UDIV) + // DIV/IDIV instructions always store the quotient in RAX and the remainder in RDX. + // Move the result to the desired register, if necessary + if (oper == GT_DIV || oper == GT_UDIV) + { + if (targetReg != REG_RAX) { - if (targetReg != REG_RAX) - { - inst_RV_RV(INS_mov, targetReg, REG_RAX, targetType); - } + inst_RV_RV(INS_mov, targetReg, REG_RAX, targetType); } - else + } + else + { + assert((oper == GT_MOD) || (oper == GT_UMOD)); + if (targetReg != REG_RDX) { - assert((oper == GT_MOD) || (oper == GT_UMOD)); - if (targetReg != REG_RDX) - { - inst_RV_RV(INS_mov, targetReg, REG_RDX, targetType); - } + inst_RV_RV(INS_mov, targetReg, REG_RDX, targetType); } } } @@ -2888,120 +2876,6 @@ CodeGen::genMultiRegCallStoreToLocal(GenTreePtr treeNode) #endif // !FEATURE_UNIX_AMD64_STRUCT_PASSING } -// Generate code for division (or mod) by power of two -// or negative powers of two. (meaning -1 * a power of two, not 2^(-1)) -// Op2 must be a contained integer constant. -void -CodeGen::genCodeForPow2Div(GenTreeOp* tree) -{ - GenTree *dividend = tree->gtOp.gtOp1; - GenTree *divisor = tree->gtOp.gtOp2; - genTreeOps oper = tree->OperGet(); - emitAttr size = emitTypeSize(tree); - emitter *emit = getEmitter(); - regNumber targetReg = tree->gtRegNum; - var_types targetType = tree->TypeGet(); - - bool isSigned = oper == GT_MOD || oper == GT_DIV; - - // precondition: extended dividend is in RDX:RAX - // which means it is either all zeros or all ones - - noway_assert(divisor->isContained()); - GenTreeIntConCommon* divImm = divisor->AsIntConCommon(); - ssize_t imm = divImm->IconValue(); - ssize_t abs_imm = abs(imm); - noway_assert(isPow2(abs_imm)); - - - if (isSigned) - { - if (imm == 1) - { - if (oper == GT_DIV) - { - if (targetReg != REG_RAX) - inst_RV_RV(INS_mov, targetReg, REG_RAX, targetType); - } - else - { - assert(oper == GT_MOD); - instGen_Set_Reg_To_Zero(size, targetReg); - } - - return; - } - - if (abs_imm == 2) - { - if (oper == GT_MOD) - { - emit->emitIns_R_I(INS_and, size, REG_RAX, 1); // result is 0 or 1 - // xor with rdx will flip all bits if negative - emit->emitIns_R_R(INS_xor, size, REG_RAX, REG_RDX); // 111.11110 or 0 - } - else - { - assert(oper == GT_DIV); - // add 1 if it's negative - emit->emitIns_R_R(INS_sub, size, REG_RAX, REG_RDX); - } - } - else - { - // add imm-1 if negative - emit->emitIns_R_I(INS_and, size, REG_RDX, abs_imm - 1); - emit->emitIns_R_R(INS_add, size, REG_RAX, REG_RDX); - } - - if (oper == GT_DIV) - { - unsigned shiftAmount = genLog2(unsigned(abs_imm)); - inst_RV_SH(INS_sar, size, REG_RAX, shiftAmount); - - if (imm < 0) - { - emit->emitIns_R(INS_neg, size, REG_RAX); - } - } - else - { - assert(oper == GT_MOD); - if (abs_imm > 2) - { - emit->emitIns_R_I(INS_and, size, REG_RAX, abs_imm - 1); - } - // RDX contains 'imm-1' if negative - emit->emitIns_R_R(INS_sub, size, REG_RAX, REG_RDX); - } - - if (targetReg != REG_RAX) - { - inst_RV_RV(INS_mov, targetReg, REG_RAX, targetType); - } - } - else - { - assert (imm > 0); - - if (targetReg != dividend->gtRegNum) - { - inst_RV_RV(INS_mov, targetReg, dividend->gtRegNum, targetType); - } - - if (oper == GT_UDIV) - { - inst_RV_SH(INS_shr, size, targetReg, genLog2(unsigned(imm))); - } - else - { - assert(oper == GT_UMOD); - - emit->emitIns_R_I(INS_and, size, targetReg, imm -1); - } - } -} - /*********************************************************************************************** * Generate code for localloc diff --git a/src/jit/lower.cpp b/src/jit/lower.cpp index 060a00ee264a..6fd97cb2e3fa 100755 --- a/src/jit/lower.cpp +++ b/src/jit/lower.cpp @@ -864,6 +864,78 @@ GenTreePtr Lowering::CreateLocalTempAsg(GenTreePtr rhs, return store; } +//----------------------------------------------------------------------------------------------- +// CreateTemporary: Store the result of the given tree in a newly created temporary local +// variable and replace the original use of the tree with the temporary. +// +// Arguments: +// ppTree - a pointer to the tree use to replace. +// +// Return Value: +// The newly created store statement. +// +// Assumptions: +// This may only be called during tree lowering. The callee must ensure that the tree has already +// been lowered and is part of compCurStmt and that compCurStmt is in compCurBB. +// +// Notes: +// The newly created statement is usually an embedded statement but it can also be a top-level +// statement if the tree to be replaced extends to the begining of the current statement. If +// a top-level statement is created any embedded statements contained in the tree move to the +// the new top-level statement, before the current statement. Such embedded statements need to +// be lowered here because the normal lowering code path won't reach them anymore. +// +// TODO-Cleanup: +// Some uses of fgInsertEmbeddedFormTemp in lowering could be replaced with this to avoid +// duplication, see LowerArrElem for example. + +GenTreeStmt* Lowering::CreateTemporary(GenTree** ppTree) +{ + GenTreeStmt* newStmt = comp->fgInsertEmbeddedFormTemp(ppTree); + + // The tree is assumed to be already lowered so the newly created statement + // should not be lowered again. + newStmt->gtFlags |= GTF_STMT_SKIP_LOWER; + + assert(newStmt->gtStmtExpr->OperIsLocalStore()); + + // If the newly created statement is top-level then we need to manually lower its embedded + // statements, the tree is lowered but some of its embedded statements are yet to be lowered. + if (newStmt->gtStmtIsTopLevel()) + { + GenTree* curStmt = comp->compCurStmt; + + for (GenTree* nextEmbeddedStmt = newStmt->gtStmtNextIfEmbedded(); + nextEmbeddedStmt != nullptr; + nextEmbeddedStmt = nextEmbeddedStmt->gtStmt.gtStmtNextIfEmbedded()) + { + // A previous call to CreateTemporary could have created embedded statements + // from the tree and those are already lowered. + if ((nextEmbeddedStmt->gtFlags & GTF_STMT_SKIP_LOWER) != 0) + continue; + +#ifdef DEBUG + if (comp->verbose) + { + printf("Lowering BB%02u, stmt id %u\n", currBlock->bbNum, nextEmbeddedStmt->gtTreeID); + } +#endif + comp->compCurStmt = nextEmbeddedStmt; + comp->fgWalkTreePost(&nextEmbeddedStmt->gtStmt.gtStmtExpr, &Lowering::LowerNodeHelper, this, true); + nextEmbeddedStmt->gtFlags |= GTF_STMT_SKIP_LOWER; + + // Lowering can remove the statement and set compCurStmt to another suitable statement. + // Currently only switch lowering does this and since embedded statements can't contain + // a GT_SWITCH this case should never be hit here. + assert(comp->compCurStmt == nextEmbeddedStmt); + } + + comp->compCurStmt = curStmt; + } + + return newStmt; +} + // This is the main entry point for Lowering. // In addition to that, LowerNode is also responsible for initializing the @@ -897,6 +969,16 @@ void Lowering::LowerNode(GenTreePtr* ppTree, Compiler::fgWalkData* data) LowerAdd(ppTree, data); break; + case GT_UDIV: + case GT_UMOD: + LowerUnsignedDivOrMod(*ppTree); + break; + + case GT_DIV: + case GT_MOD: + LowerSignedDivOrMod(ppTree, data); + break; + case GT_SWITCH: LowerSwitch(ppTree); break; @@ -3761,6 +3843,198 @@ void Lowering::LowerAdd(GenTreePtr* pTree, Compiler::fgWalkData* data) #endif // !_TARGET_ARMARCH_ } +//------------------------------------------------------------------------ +// LowerUnsignedDivOrMod: transform GT_UDIV/GT_UMOD nodes with a const power of 2 +// divisor into GT_RSZ/GT_AND nodes. +// +// Arguments: +// tree: pointer to the GT_UDIV/GT_UMOD node to be lowered + +void Lowering::LowerUnsignedDivOrMod(GenTree* tree) +{ + assert(tree->OperGet() == GT_UDIV || tree->OperGet() == GT_UMOD); + + GenTree* divisor = tree->gtGetOp2(); + + if (divisor->IsCnsIntOrI()) + { + size_t divisorValue = static_cast(divisor->gtIntCon.IconValue()); + + if (isPow2(divisorValue)) + { + genTreeOps newOper; + + if (tree->OperGet() == GT_UDIV) + { + newOper = GT_RSZ; + divisorValue = genLog2(divisorValue); + } + else + { + newOper = GT_AND; + divisorValue -= 1; + } + + tree->SetOper(newOper); + divisor->gtIntCon.SetIconValue(divisorValue); + } + } +} + +//------------------------------------------------------------------------ +// LowerSignedDivOrMod: transform integer GT_DIV/GT_MOD nodes with a power of 2 +// const divisor into equivalent but faster sequences. +// +// Arguments: +// pTree: pointer to the parent node's link to the node we care about + +void Lowering::LowerSignedDivOrMod(GenTreePtr* ppTree, Compiler::fgWalkData* data) +{ + GenTree* divMod = *ppTree; + assert(divMod->OperGet() == GT_DIV || divMod->OperGet() == GT_MOD); + GenTree* divisor = divMod->gtGetOp2(); + + if (divisor->IsCnsIntOrI()) + { + const var_types type = divMod->TypeGet(); + assert(type == TYP_INT || type == TYP_LONG); + + GenTree* dividend = divMod->gtGetOp1(); + + if (dividend->IsCnsIntOrI()) + { + // We shouldn't see a divmod with constant operands here but if we do then it's likely + // because optimizations are disabled or it's a case that's supposed to throw an exception. + // Don't optimize this. + return; + } + + ssize_t divisorValue = divisor->gtIntCon.IconValue(); + + if (divisorValue == -1) + { + // x / -1 can't be optimized because INT_MIN / -1 is required to throw an exception. + + // x % -1 is always 0 and the IL spec says that the rem instruction "can" throw an exception if x is + // the minimum representable integer. However, the C# spec says that an exception "is" thrown in this + // case so optimizing this case would break C# code. + + // A runtime check could be used to handle this case but it's probably too rare to matter. + return; + } + + bool isDiv = divMod->OperGet() == GT_DIV; + + if (isDiv) + { + if ((type == TYP_INT && divisorValue == INT_MIN) || + (type == TYP_LONG && divisorValue == INT64_MIN)) + { + // If the divisor is the minimum representable integer value then we can use a compare, + // the result is 1 iff the dividend equals divisor. + divMod->SetOper(GT_EQ); + return; + } + } + + size_t absDivisorValue = (divisorValue == SSIZE_T_MIN) ? static_cast(divisorValue) : static_cast(abs(divisorValue)); + + if (isPow2(absDivisorValue)) + { + // We need to use the dividend node multiple times so its value needs to be + // computed once and stored in a temp variable. + CreateTemporary(&(divMod->gtOp.gtOp1)); + dividend = divMod->gtGetOp1(); + + GenTreeStmt* curStmt = comp->compCurStmt->AsStmt(); + unsigned curBBWeight = currBlock->getBBWeight(comp); + unsigned dividendLclNum = dividend->gtLclVar.gtLclNum; + + GenTree* adjustment = comp->gtNewOperNode( + GT_RSH, type, + dividend, + comp->gtNewIconNode(type == TYP_INT ? 31 : 63)); + + if (absDivisorValue == 2) + { + // If the divisor is +/-2 then we'd end up with a bitwise and between 0/-1 and 1. + // We can get the same result by using GT_RSZ instead of GT_RSH. + adjustment->SetOper(GT_RSZ); + } + else + { + adjustment = comp->gtNewOperNode( + GT_AND, type, + adjustment, + comp->gtNewIconNode(absDivisorValue - 1, type)); + } + + GenTree* adjustedDividend = comp->gtNewOperNode( + GT_ADD, type, + adjustment, + comp->gtNewLclvNode(dividendLclNum, type)); + + comp->lvaTable[dividendLclNum].incRefCnts(curBBWeight, comp); + + GenTree* newDivMod; + + if (isDiv) + { + // perform the division by right shifting the adjusted dividend + divisor->gtIntCon.SetIconValue(genLog2(absDivisorValue)); + + newDivMod = comp->gtNewOperNode( + GT_RSH, type, + adjustedDividend, + divisor); + + if (divisorValue < 0) + { + // negate the result if the divisor is negative + newDivMod = comp->gtNewOperNode( + GT_NEG, type, + newDivMod); + } + } + else + { + // divisor % dividend = dividend - divisor x (dividend / divisor) + // divisor x (dividend / divisor) translates to (dividend >> log2(divisor)) << log2(divisor) + // which simply discards the low log2(divisor) bits, that's just dividend & ~(divisor - 1) + divisor->gtIntCon.SetIconValue(~(absDivisorValue - 1)); + + newDivMod = comp->gtNewOperNode( + GT_SUB, type, + comp->gtNewLclvNode(dividendLclNum, type), + comp->gtNewOperNode( + GT_AND, type, + adjustedDividend, + divisor)); + + comp->lvaTable[dividendLclNum].incRefCnts(curBBWeight, comp); + } + + // Remove the divisor and dividend nodes from the linear order, + // since we have reused them and will resequence the tree + comp->fgSnipNode(curStmt, divisor); + comp->fgSnipNode(curStmt, dividend); + + // linearize and insert the new tree before the original divMod node + comp->gtSetEvalOrder(newDivMod); + comp->fgSetTreeSeq(newDivMod); + comp->fgInsertTreeInListBefore(newDivMod, divMod, curStmt); + comp->fgSnipNode(curStmt, divMod); + + // the divMod that we've replaced could have been a call arg + comp->fgFixupIfCallArg(data->parentStack, divMod, newDivMod); + + // replace the original divmod node with the new divmod tree + *ppTree = newDivMod; + + return; + } + } +} //------------------------------------------------------------------------ // LowerInd: attempt to transform indirected expression into an addressing mode @@ -4040,9 +4314,6 @@ void Lowering::DoPhase() comp->lvaPromoteLongVars(); #endif // !defined(_TARGET_64BIT_) -#ifdef DEBUG - unsigned stmtNum = 0; -#endif for (BasicBlock* block = comp->fgFirstBB; block; block = block->bbNext) { GenTreePtr stmt; @@ -4062,11 +4333,9 @@ void Lowering::DoPhase() continue; } #ifdef DEBUG - ++stmtNum; if (comp->verbose) { - // This is a useful location for a conditional breakpoint in Visual Studio (i.e. when stmtNum == 15) - printf("Decomposing BB%02u, stmt %u\n", block->bbNum, stmtNum); + printf("Decomposing BB%02u, stmt id %u\n", block->bbNum, stmt->gtTreeID); } #endif comp->compCurStmt = stmt; @@ -4074,9 +4343,6 @@ void Lowering::DoPhase() } #endif //!_TARGET_64BIT_ -#ifdef DEBUG - stmtNum = 0; -#endif // Walk the statement trees in this basic block for (stmt = block->bbTreeList; stmt; stmt = stmt->gtNext) { @@ -4085,11 +4351,9 @@ void Lowering::DoPhase() continue; } #ifdef DEBUG - ++stmtNum; - if (comp->verbose) + if (comp->verbose) { - // This is a useful location for a conditional breakpoint in Visual Studio (i.e. when stmtNum == 15) - printf("Lowering BB%02u, stmt %u\n", block->bbNum, stmtNum); + printf("Lowering BB%02u, stmt id %u\n", block->bbNum, stmt->gtTreeID); } #endif comp->compCurStmt = stmt; diff --git a/src/jit/lower.h b/src/jit/lower.h index 6cd5e547e7cd..e9a3e65c7f84 100644 --- a/src/jit/lower.h +++ b/src/jit/lower.h @@ -165,6 +165,8 @@ class Lowering : public Phase void LowerInd(GenTreePtr* ppTree); void LowerAddrMode(GenTreePtr* ppTree, GenTree* before, Compiler::fgWalkData* data, bool isIndir); void LowerAdd(GenTreePtr* ppTree, Compiler::fgWalkData* data); + void LowerUnsignedDivOrMod(GenTree* tree); + void LowerSignedDivOrMod(GenTreePtr* ppTree, Compiler::fgWalkData* data); // Remove the nodes that are no longer used after an addressing mode is constructed under a GT_IND void LowerIndCleanupHelper(GenTreeAddrMode* addrMode, GenTreePtr tree); @@ -201,7 +203,7 @@ class Lowering : public Phase static bool NodesAreEquivalentLeaves (GenTreePtr candidate, GenTreePtr storeInd); GenTreePtr CreateLocalTempAsg (GenTreePtr rhs, unsigned refCount, GenTreePtr *ppLclVar = nullptr); - + GenTreeStmt* CreateTemporary (GenTree** ppTree); bool AreSourcesPossiblyModified (GenTree* use, GenTree* src1, GenTree *src2); void ReplaceNode (GenTree** ppTreeLocation, GenTree* replacementNode, diff --git a/src/jit/lowerarm64.cpp b/src/jit/lowerarm64.cpp index 0aaa0b9aa6b3..ad0ae9de5593 100644 --- a/src/jit/lowerarm64.cpp +++ b/src/jit/lowerarm64.cpp @@ -367,7 +367,6 @@ void Lowering::TreeNodeInfoInit(GenTree* stmt) case GT_MULHI: case GT_UDIV: { - // TODO-ARM64-CQ: Optimize a divide by power of 2 as we do for AMD64 info->srcCount = 2; info->dstCount = 1; } diff --git a/src/jit/lowerxarch.cpp b/src/jit/lowerxarch.cpp index 64abf1ec9c44..8cd9d43d19bc 100644 --- a/src/jit/lowerxarch.cpp +++ b/src/jit/lowerxarch.cpp @@ -575,31 +575,6 @@ void Lowering::TreeNodeInfoInit(GenTree* stmt) op1 = tree->gtOp.gtOp1; op2 = tree->gtOp.gtOp2; - // See if we have an optimizable power of 2 which will be expanded - // using instructions other than division. - // (fgMorph has already done magic number transforms) - - if (op2->IsIntCnsFitsInI32()) - { - bool isSigned = tree->OperGet() == GT_MOD || tree->OperGet() == GT_DIV; - ssize_t amount = op2->gtIntConCommon.IconValue(); - - if (isPow2(abs(amount)) && (isSigned || amount > 0) - && amount != -1) - { - MakeSrcContained(tree, op2); - - if (isSigned) - { - // we are going to use CDQ instruction so want these RDX:RAX - 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); - } - break; - } - } - // Amd64 Div/Idiv instruction: // Dividend in RAX:RDX and computes // Quotient in RAX, Remainder in RDX diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp index a80640c769a0..a3fb8bd2677e 100644 --- a/src/jit/morph.cpp +++ b/src/jit/morph.cpp @@ -12752,7 +12752,7 @@ bool Compiler::fgShouldUseMagicNumberDivide(GenTreeOp* tree) return false; // codegen will expand these - if (isPow2(cons)) + if (cons == SSIZE_T_MIN || isPow2(abs(cons))) return false; // someone else will fold this away, so don't make it complicated for them diff --git a/tests/src/JIT/CodeGenBringUpTests/DivConst.cs b/tests/src/JIT/CodeGenBringUpTests/DivConst.cs new file mode 100644 index 000000000000..38b15e9cdc6f --- /dev/null +++ b/tests/src/JIT/CodeGenBringUpTests/DivConst.cs @@ -0,0 +1,442 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.CompilerServices; + +static class DivConst +{ + // I4 + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_Div_0(int i4) + { + return i4 / 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_Div_1(int i4) + { + return i4 / 1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_Div_Minus1(int i4) + { + return i4 / -1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_Div_3(int i4) + { + return i4 / 3; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_DivRef_5(ref int i4) + { + return i4 / 5; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_Div_7(int i4) + { + return i4 / 7; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_Div_Minus3(int i4) + { + return i4 / -3; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_DivPow2_2(int i4) + { + return i4 / 2; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_DivPow2_Minus2(int i4) + { + return i4 / -2; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_DivPow2_8(ref int i4) + { + return i4 / 8; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_DivPow2_Minus4(int i4) + { + return i4 / -4; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_DivPow2_I4Min(int i4) + { + return i4 / int.MinValue; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_DivPow2Embedded_4(int x, int y) + { + return y * 2 + (x + 2) / 4 + (x * y >> 31); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_DivPow2Embdedded_Point(Point p) + { + int a = p.X + 4; + int b = (a - p.Y) / 2; + return p.Y > p.X ? a : b; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_DivPow2Call_8(int i4) + { + return I4_DivPow2_2(i4 / 8) + I4_DivRef_5(ref i4) / 8; + } + + // I8 + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_Div_0(long i8) + { + return i8 / 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_Div_1(long i8) + { + return i8 / 1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_Div_Minus1(long i8) + { + return i8 / -1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_Div_3(long i8) + { + return i8 / 3; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_Div_5(long i8) + { + return i8 / 5; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_Div_7(ref long i8) + { + return i8 / 7; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_Div_Minus3(long i8) + { + return i8 / -3; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_DivPow2_4(long i8) + { + return i8 / 4; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_DivPow2_Minus8(long i8) + { + return i8 / -8; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_DivUncontainedPow2_1Shl32(long i8) + { + return i8 / (1L << 32); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_DivUncontainedPow2_I8Min(long i8) + { + return i8 / long.MinValue; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_DivPow2Embedded_4(long x, long y) + { + return y * 2 + (x + 2) / 4 + (x * y >> 31); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_DivPow2Call_8(long i8) + { + return I8_DivPow2_4(i8 / 8) + I8_Div_5(i8) / 8; + } +} + +class Point +{ + public int X; + public int Y; +} + +static class DivProgram +{ + public static int Main() + { + const int Pass = 100; + const int Fail = -1; + + // I4 + + try + { + DivConst.I4_Div_0(42); + return Fail; + } + catch (DivideByZeroException) + { + } + catch (Exception) + { + return Fail; + } + + if (DivConst.I4_Div_1(42) != 42) + { + return Fail; + } + + if (DivConst.I4_Div_Minus1(42) != -42) + { + return Fail; + } + + try + { + DivConst.I4_Div_Minus1(int.MinValue); + return Fail; + } + catch (OverflowException) + { + } + catch (Exception) + { + return Fail; + } + + if (DivConst.I4_Div_3(42) != 14) + { + return Fail; + } + + { + int dividend = 42; + + if (DivConst.I4_DivRef_5(ref dividend) != 8) + { + return Fail; + } + } + + if (DivConst.I4_Div_7(42) != 6) + { + return Fail; + } + + if (DivConst.I4_Div_Minus3(42) != -14) + { + return Fail; + } + + if (DivConst.I4_DivPow2_2(42) != 21) + { + return Fail; + } + + if (DivConst.I4_DivPow2_2(43) != 21) + { + return Fail; + } + + if (DivConst.I4_DivPow2_2(-42) != -21) + { + return Fail; + } + + if (DivConst.I4_DivPow2_2(-43) != -21) + { + return Fail; + } + + if (DivConst.I4_DivPow2_Minus2(43) != -21) + { + return Fail; + } + + { + int dividend = 42; + + if (DivConst.I4_DivPow2_8(ref dividend) != 5) + { + return Fail; + } + } + + { + int dividend = -42; + + if (DivConst.I4_DivPow2_8(ref dividend) != -5) + { + return Fail; + } + } + + if (DivConst.I4_DivPow2_Minus4(42) != -10) + { + return Fail; + } + + if (DivConst.I4_DivPow2_Minus4(-42) != 10) + { + return Fail; + } + + if (DivConst.I4_DivPow2_I4Min(-42) != 0) + { + return Fail; + } + + if (DivConst.I4_DivPow2_I4Min(int.MinValue) != 1) + { + return Fail; + } + + if (DivConst.I4_DivPow2Embedded_4(420, 938) != 1981) + { + return Fail; + } + + if (DivConst.I4_DivPow2Embdedded_Point(new Point { X = 513, Y = 412 }) != 52) + { + return Fail; + } + + if (DivConst.I4_DivPow2Call_8(420) != 36) + { + return Fail; + } + + // I8 + + try + { + DivConst.I8_Div_0(42); + return Fail; + } + catch (DivideByZeroException) + { + } + catch (Exception) + { + return Pass; + } + + if (DivConst.I8_Div_1(42) != 42) + { + return Fail; + } + + if (DivConst.I8_Div_Minus1(42) != -42) + { + return Fail; + } + + try + { + DivConst.I8_Div_Minus1(long.MinValue); + return Fail; + } + catch (OverflowException) + { + } + catch (Exception) + { + return Fail; + } + + if (DivConst.I8_Div_3(42) != 14) + { + return Fail; + } + + if (DivConst.I8_Div_5(42) != 8) + { + return Fail; + } + + { + long dividend = 45; + + if (DivConst.I8_Div_7(ref dividend) != 6) + { + return Fail; + } + } + + if (DivConst.I8_Div_Minus3(42) != -14) + { + return Fail; + } + + if (DivConst.I8_DivPow2_4(42) != 10) + { + return Fail; + } + + if (DivConst.I8_DivPow2_Minus8(42) != -5) + { + return Fail; + } + + if (DivConst.I8_DivPow2_Minus8(-42) != 5) + { + return Fail; + } + + if (DivConst.I8_DivUncontainedPow2_1Shl32(1L << 33) != 2) + { + return Fail; + } + + if (DivConst.I8_DivUncontainedPow2_I8Min(42) != 0) + { + return Fail; + } + + if (DivConst.I8_DivUncontainedPow2_I8Min(long.MinValue) != 1) + { + return Fail; + } + + if (DivConst.I8_DivPow2Embedded_4(420, 938) != 1981) + { + return Fail; + } + + if (DivConst.I8_DivPow2Call_8(420) != 23) + { + return Fail; + } + + return Pass; + } +} diff --git a/tests/src/JIT/CodeGenBringUpTests/DivConst.csproj b/tests/src/JIT/CodeGenBringUpTests/DivConst.csproj new file mode 100644 index 000000000000..58b6823baef1 --- /dev/null +++ b/tests/src/JIT/CodeGenBringUpTests/DivConst.csproj @@ -0,0 +1,43 @@ + + + + + Debug + AnyCPU + 2.0 + {EC6FD253-5247-4D8C-887E-453B5647A0A7} + Exe + Properties + 512 + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + $(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages + ..\..\ + 7a9bfb7d + + + + + + + + + False + + + + + + + + + + + + + $(JitPackagesConfigFileDirectory)threading+thread\project.json + $(JitPackagesConfigFileDirectory)threading+thread\project.lock.json + + + + + \ No newline at end of file diff --git a/tests/src/JIT/CodeGenBringUpTests/ModConst.cs b/tests/src/JIT/CodeGenBringUpTests/ModConst.cs new file mode 100644 index 000000000000..4552fed1b7f1 --- /dev/null +++ b/tests/src/JIT/CodeGenBringUpTests/ModConst.cs @@ -0,0 +1,427 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.CompilerServices; + +static class ModConst +{ + // I4 + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_Mod_0(int i4) + { + return i4 % 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_Mod_1(int i4) + { + return i4 % 1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_Mod_Minus1(int i4) + { + return i4 % -1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_Mod_3(int i4) + { + return i4 % 3; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_ModRef_5(ref int i4) + { + return i4 % 5; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_Mod_7(int i4) + { + return i4 % 7; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_Mod_Minus3(int i4) + { + return i4 % -3; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_ModPow2_2(int i4) + { + return i4 % 2; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_ModPow2_Minus2(int i4) + { + return i4 % -2; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_ModPow2_8(ref int i4) + { + return i4 % 8; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_ModPow2_Minus4(int i4) + { + return i4 % -4; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_ModPow2_I4Min(ref int i4) + { + return i4 % int.MinValue; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_ModPow2Embedded_4(int x, int y) + { + return y * 2 + (x + 2) % 4 + (x * y >> 31); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int I4_ModPow2Call_8(int i4) + { + return I4_ModPow2_2(i4 % 8) + I4_ModRef_5(ref i4) % 8; + } + + // I8 + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_Mod_0(long i8) + { + return i8 % 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_Mod_1(long i8) + { + return i8 % 1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_Mod_Minus1(long i8) + { + return i8 % -1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_Mod_3(long i8) + { + return i8 % 3; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_Mod_5(long i8) + { + return i8 % 5; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_Mod_7(long i8) + { + return i8 % 7; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_Mod_Minus3(long i8) + { + return i8 % -3; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_ModPow2_4(long i8) + { + return i8 % 4; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_ModPow2_Minus8(long i8) + { + return i8 % -8; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_ModUncontainedPow2_1Shl32(long i8) + { + return i8 % (1L << 32); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_ModUncontainedPow2_I8Min(long i8) + { + return i8 % long.MinValue; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_ModPow2Embedded_4(long x, long y) + { + return y * 2 + (x + 2) % 4 + (x * y >> 31); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static long I8_ModPow2Call_8(long i8) + { + return I8_ModPow2_4(i8 % 8) + I8_Mod_5(i8) % 8; + } +} + +static class ModProgram +{ + public static int Main() + { + const int Pass = 100; + const int Fail = -1; + + // I4 + + try + { + ModConst.I4_Mod_0(42); + return Fail; + } + catch (DivideByZeroException) + { + } + catch (Exception) + { + return Fail; + } + + if (ModConst.I4_Mod_1(42) != 0) + { + return Fail; + } + + if (ModConst.I4_Mod_Minus1(42) != 0) + { + return Fail; + } + + try + { + ModConst.I4_Mod_Minus1(int.MinValue); + return Fail; + } + catch (OverflowException) + { + } + catch (Exception) + { + return Fail; + } + + if (ModConst.I4_Mod_3(41) != 2) + { + return Fail; + } + + { + int dividend = 42; + + if (ModConst.I4_ModRef_5(ref dividend) != 2) + { + return Fail; + } + } + + if (ModConst.I4_Mod_7(42) != 0) + { + return Fail; + } + + if (ModConst.I4_Mod_Minus3(41) != 2) + { + return Fail; + } + + if (ModConst.I4_ModPow2_2(43) != 1) + { + return Fail; + } + + if (ModConst.I4_ModPow2_2(42) != 0) + { + return Fail; + } + + if (ModConst.I4_ModPow2_2(-43) != -1) + { + return Fail; + } + + if (ModConst.I4_ModPow2_2(-42) != 0) + { + return Fail; + } + + if (ModConst.I4_ModPow2_Minus2(43) != 1) + { + return Fail; + } + + { + int dividend = 42; + + if (ModConst.I4_ModPow2_8(ref dividend) != 2) + { + return Fail; + } + } + + { + int dividend = -42; + + if (ModConst.I4_ModPow2_8(ref dividend) != -2) + { + return Fail; + } + } + + if (ModConst.I4_ModPow2_Minus4(42) != 2) + { + return Fail; + } + + if (ModConst.I4_ModPow2_Minus4(-42) != -2) + { + return Fail; + } + + { + int dividend = -42; + + if (ModConst.I4_ModPow2_I4Min(ref dividend) != -42) + { + return Fail; + } + } + + { + int dividend = int.MinValue; + + if (ModConst.I4_ModPow2_I4Min(ref dividend) != 0) + { + return Fail; + } + } + + if (ModConst.I4_ModPow2Embedded_4(420, 938) != 1878) + { + return Fail; + } + + if (ModConst.I4_ModPow2Call_8(3674) != 4) + { + return Fail; + } + + // I8 + + try + { + ModConst.I8_Mod_0(42); + return Fail; + } + catch (DivideByZeroException) + { + } + catch (Exception) + { + return Pass; + } + + if (ModConst.I8_Mod_1(42) != 0) + { + return Fail; + } + + if (ModConst.I8_Mod_Minus1(42) != 0) + { + return Fail; + } + + try + { + ModConst.I8_Mod_Minus1(long.MinValue); + return Fail; + } + catch (OverflowException) + { + } + catch (Exception) + { + return Fail; + } + + if (ModConst.I8_Mod_3(43) != 1) + { + return Fail; + } + + if (ModConst.I8_Mod_5(42) != 2) + { + return Fail; + } + + if (ModConst.I8_Mod_7(45) != 3) + { + return Fail; + } + + if (ModConst.I8_Mod_Minus3(-43) != -1) + { + return Fail; + } + + if (ModConst.I8_ModPow2_4(42) != 2) + { + return Fail; + } + + if (ModConst.I8_ModPow2_Minus8(42) != 2) + { + return Fail; + } + + if (ModConst.I8_ModPow2_Minus8(-42) != -2) + { + return Fail; + } + + if (ModConst.I8_ModUncontainedPow2_1Shl32((1L << 33) + 42L) != 42) + { + return Fail; + } + + if (ModConst.I8_ModUncontainedPow2_I8Min(42) != 42) + { + return Fail; + } + + if (ModConst.I8_ModUncontainedPow2_I8Min(long.MinValue) != 0) + { + return Fail; + } + + if (ModConst.I8_ModPow2Embedded_4(420, 938) != 1878) + { + return Fail; + } + + if (ModConst.I8_ModPow2Call_8(3674) != 6) + { + return Fail; + } + + return Pass; + } +} diff --git a/tests/src/JIT/CodeGenBringUpTests/ModConst.csproj b/tests/src/JIT/CodeGenBringUpTests/ModConst.csproj new file mode 100644 index 000000000000..7e04c9223b45 --- /dev/null +++ b/tests/src/JIT/CodeGenBringUpTests/ModConst.csproj @@ -0,0 +1,43 @@ + + + + + Debug + AnyCPU + 2.0 + {314823E3-7BB2-4C19-BA04-5AC565215BA3} + Exe + Properties + 512 + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + $(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages + ..\..\ + 7a9bfb7d + + + + + + + + + False + + + + + + + + + + + + + $(JitPackagesConfigFileDirectory)threading+thread\project.json + $(JitPackagesConfigFileDirectory)threading+thread\project.lock.json + + + + + \ No newline at end of file diff --git a/tests/src/JIT/CodeGenBringUpTests/UDivConst.cs b/tests/src/JIT/CodeGenBringUpTests/UDivConst.cs new file mode 100644 index 000000000000..cd507fe55425 --- /dev/null +++ b/tests/src/JIT/CodeGenBringUpTests/UDivConst.cs @@ -0,0 +1,219 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.CompilerServices; + +static class UDivConst +{ + // U4 + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint U4_Div_0(uint u4) + { + return u4 / 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint U4_Div_1(uint u4) + { + return u4 / 1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint U4_Div_3(uint u4) + { + return u4 / 3; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint U4_Div_5(uint u4) + { + return u4 / 5; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint U4_Div_7(uint u4) + { + return u4 / 7; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint U4_DivPow2_16(uint u4) + { + return u4 / 16; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint U4_DivPow2_I4Min(uint u4) + { + return u4 / 0x80000000u; + } + + // U8 + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ulong U8_Div_0(ulong u8) + { + return u8 / 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ulong U8_Div_1(ulong u8) + { + return u8 / 1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ulong U8_Div_3(ulong u8) + { + return u8 / 3; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ulong U8_Div_5(ulong u8) + { + return u8 / 5; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ulong U8_Div_7(ulong u8) + { + return u8 / 7; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ulong U8_DivUncontained_I8Max(ulong u8) + { + return u8 / ulong.MaxValue; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ulong U8_DivPow2_2(ulong u8) + { + return u8 / 2; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ulong U8_DivUncontainedPow2_1Shl32(ulong u8) + { + return u8 / (1UL << 32); + } +} + +static class UDivProgram +{ + public static int Main() + { + const int Pass = 100; + const int Fail = -1; + + // U4 + + try + { + UDivConst.U4_Div_0(42); + return Fail; + } + catch (DivideByZeroException) + { + } + catch (Exception) + { + return Fail; + } + + if (UDivConst.U4_Div_1(42) != 42) + { + return Fail; + } + + if (UDivConst.U4_Div_3(42) != 14) + { + return Fail; + } + + if (UDivConst.U4_Div_5(42) != 8) + { + return Fail; + } + + if (UDivConst.U4_Div_7(43) != 6) + { + return Fail; + } + + if (UDivConst.U4_DivPow2_16(42) != 2) + { + return Fail; + } + + if (UDivConst.U4_DivPow2_I4Min(3) != 0) + { + return Fail; + } + + if (UDivConst.U4_DivPow2_I4Min(0x80000001u) != 1) + { + return Fail; + } + + // U8 + + try + { + UDivConst.U8_Div_0(42); + return Fail; + } + catch (DivideByZeroException) + { + } + catch (Exception) + { + return Fail; + } + + if (UDivConst.U8_Div_1(42) != 42) + { + return Fail; + } + + if (UDivConst.U8_Div_3(42) != 14) + { + return Fail; + } + + if (UDivConst.U8_Div_5(42) != 8) + { + return Fail; + } + + if (UDivConst.U8_Div_7(420) != 60) + { + return Fail; + } + + if (UDivConst.U8_DivUncontained_I8Max(ulong.MaxValue - 1) != 0) + { + return Fail; + } + + if (UDivConst.U8_DivUncontained_I8Max(ulong.MaxValue) != 1) + { + return Fail; + } + + if (UDivConst.U8_DivPow2_2(42) != 21) + { + return Fail; + } + + if (UDivConst.U8_DivUncontainedPow2_1Shl32(1UL << 33) != 2) + { + return Fail; + } + + return Pass; + } +} diff --git a/tests/src/JIT/CodeGenBringUpTests/UDivConst.csproj b/tests/src/JIT/CodeGenBringUpTests/UDivConst.csproj new file mode 100644 index 000000000000..81a799ad87d3 --- /dev/null +++ b/tests/src/JIT/CodeGenBringUpTests/UDivConst.csproj @@ -0,0 +1,43 @@ + + + + + Debug + AnyCPU + 2.0 + {53C70B64-C175-43BF-A98A-719868D6D695} + Exe + Properties + 512 + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + $(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages + ..\..\ + 7a9bfb7d + + + + + + + + + False + + + + + + + + + + + + + $(JitPackagesConfigFileDirectory)threading+thread\project.json + $(JitPackagesConfigFileDirectory)threading+thread\project.lock.json + + + + + \ No newline at end of file diff --git a/tests/src/JIT/CodeGenBringUpTests/UModConst.cs b/tests/src/JIT/CodeGenBringUpTests/UModConst.cs new file mode 100644 index 000000000000..06cc28d8a829 --- /dev/null +++ b/tests/src/JIT/CodeGenBringUpTests/UModConst.cs @@ -0,0 +1,224 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.CompilerServices; + +static class UModConst +{ + // U4 + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint U4_Mod_0(uint u4) + { + return u4 % 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint U4_Mod_1(uint u4) + { + return u4 % 1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint U4_Mod_3(uint u4) + { + return u4 % 3; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint U4_Mod_5(uint u4) + { + return u4 % 5; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint U4_Mod_7(uint u4) + { + return u4 % 7; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint U4_ModPow2_16(uint u4) + { + return u4 % 16; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint U4_ModPow2_0x80000000(uint u4) + { + return u4 % 0x80000000u; + } + + // U8 + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ulong U8_Mod_0(ulong u8) + { + return u8 % 0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ulong U8_Mod_1(ulong u8) + { + return u8 % 1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ulong U8_Mod_3(ulong u8) + { + return u8 % 3; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ulong U8_Mod_5(ulong u8) + { + return u8 % 5; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ulong U8_Mod_7(ulong u8) + { + return u8 % 7; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ulong U8_ModUncontained_I8Max(ulong u8) + { + return u8 % ulong.MaxValue; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ulong U8_ModPow2_8(ulong u8) + { + return u8 % 8; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ulong U8_ModUncontainedPow2_1Shl32(ulong u8) + { + return u8 % (1UL << 32); + } +} + +static class UModProgram +{ + public static int Main() + { + const int Pass = 100; + const int Fail = -1; + + // U4 + + try + { + UModConst.U4_Mod_0(42); + return Fail; + } + catch (DivideByZeroException) + { + } + catch (Exception) + { + return Fail; + } + + if (UModConst.U4_Mod_1(42) != 0) + { + return Fail; + } + + if (UModConst.U4_Mod_3(43) != 1) + { + return Fail; + } + + if (UModConst.U4_Mod_5(42) != 2) + { + return Fail; + } + + if (UModConst.U4_Mod_7(43) != 1) + { + return Fail; + } + + if (UModConst.U4_ModPow2_16(42) != 10) + { + return Fail; + } + + if (UModConst.U4_ModPow2_0x80000000(3) != 3) + { + return Fail; + } + + if (UModConst.U4_ModPow2_0x80000000(0x80000001u) != 1) + { + return Fail; + } + + // U8 + + try + { + UModConst.U8_Mod_0(42); + return Fail; + } + catch (DivideByZeroException) + { + } + catch (Exception) + { + return Fail; + } + + if (UModConst.U8_Mod_1(42) != 0) + { + return Fail; + } + + if (UModConst.U8_Mod_3(43) != 1) + { + return Fail; + } + + if (UModConst.U8_Mod_5(42) != 2) + { + return Fail; + } + + if (UModConst.U8_Mod_7(420) != 0) + { + return Fail; + } + + if (UModConst.U8_ModUncontained_I8Max(ulong.MaxValue - 1) != ulong.MaxValue - 1) + { + return Fail; + } + + if (UModConst.U8_ModUncontained_I8Max(ulong.MaxValue) != 0) + { + return Fail; + } + + if (UModConst.U8_ModPow2_8(42) != 2) + { + return Fail; + } + + if (UModConst.U8_ModPow2_8(43) != 3) + { + return Fail; + } + + if (UModConst.U8_ModUncontainedPow2_1Shl32((1UL << 33) + 42) != 42) + { + return Fail; + } + + return Pass; + } +} diff --git a/tests/src/JIT/CodeGenBringUpTests/UModConst.csproj b/tests/src/JIT/CodeGenBringUpTests/UModConst.csproj new file mode 100644 index 000000000000..942ffbe8cb18 --- /dev/null +++ b/tests/src/JIT/CodeGenBringUpTests/UModConst.csproj @@ -0,0 +1,43 @@ + + + + + Debug + AnyCPU + 2.0 + {D7512FD8-08E1-4BB6-8701-E3FEEAE4FF66} + Exe + Properties + 512 + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + $(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages + ..\..\ + 7a9bfb7d + + + + + + + + + False + + + + + + + + + + + + + $(JitPackagesConfigFileDirectory)threading+thread\project.json + $(JitPackagesConfigFileDirectory)threading+thread\project.lock.json + + + + + \ No newline at end of file