diff --git a/src/coreclr/System.Private.CoreLib/CompatibilitySuppressions.xml b/src/coreclr/System.Private.CoreLib/CompatibilitySuppressions.xml
index 6a201651f126a..ae179baa9c50e 100644
--- a/src/coreclr/System.Private.CoreLib/CompatibilitySuppressions.xml
+++ b/src/coreclr/System.Private.CoreLib/CompatibilitySuppressions.xml
@@ -1,3 +1,24 @@
+
+
+ CP0001
+ T:Internal.Console
+
+
+ CP0001
+ T:System.Runtime.CompilerServices.ICastable
+
+
+ CP0002
+ F:System.Resources.ResourceManager.BaseNameField
+
+
+ CP0002
+ F:System.Resources.ResourceSet.Reader
+
+
+ CP0014
+ M:System.Runtime.InteropServices.Marshal.CreateWrapperOfType(System.Object,System.Type)->object?:[T:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute]
+
\ No newline at end of file
diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h
index e565c419f06ab..54dd040b20e53 100644
--- a/src/coreclr/jit/compiler.h
+++ b/src/coreclr/jit/compiler.h
@@ -3925,6 +3925,7 @@ class Compiler
GenTree* addRangeCheckIfNeeded(
NamedIntrinsic intrinsic, GenTree* immOp, bool mustExpand, int immLowerBound, int immUpperBound);
GenTree* addRangeCheckForHWIntrinsic(GenTree* immOp, int immLowerBound, int immUpperBound);
+
#endif // FEATURE_HW_INTRINSICS
GenTree* impArrayAccessIntrinsic(CORINFO_CLASS_HANDLE clsHnd,
CORINFO_SIG_INFO* sig,
diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp
index 0edd913a00bab..8413aab972054 100644
--- a/src/coreclr/jit/gentree.cpp
+++ b/src/coreclr/jit/gentree.cpp
@@ -19090,6 +19090,8 @@ bool GenTree::isRMWHWIntrinsic(Compiler* comp)
case NI_FMA_MultiplySubtractNegated:
case NI_FMA_MultiplySubtractNegatedScalar:
case NI_FMA_MultiplySubtractScalar:
+ case NI_X86Base_DivRem:
+ case NI_X86Base_X64_DivRem:
{
return true;
}
@@ -23943,6 +23945,12 @@ ClassLayout* GenTreeHWIntrinsic::GetLayout(Compiler* compiler) const
switch (GetHWIntrinsicId())
{
+#ifdef TARGET_XARCH
+ case NI_X86Base_DivRem:
+ return compiler->typGetBlkLayout(genTypeSize(GetSimdBaseType()) * 2);
+ case NI_X86Base_X64_DivRem:
+ return compiler->typGetBlkLayout(16);
+#endif // TARGET_XARCH
#ifdef TARGET_ARM64
case NI_AdvSimd_Arm64_LoadPairScalarVector64:
case NI_AdvSimd_Arm64_LoadPairScalarVector64NonTemporal:
diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h
index 1e3f87248589d..2da896ef3283b 100644
--- a/src/coreclr/jit/gentree.h
+++ b/src/coreclr/jit/gentree.h
@@ -3683,8 +3683,8 @@ static const unsigned PACKED_GTF_SPILLED = 2;
//
inline GenTreeFlags GetMultiRegSpillFlagsByIdx(MultiRegSpillFlags flags, unsigned idx)
{
- static_assert_no_msg(MAX_RET_REG_COUNT * 2 <= sizeof(unsigned char) * BITS_PER_BYTE);
- assert(idx < MAX_RET_REG_COUNT);
+ static_assert_no_msg(MAX_MULTIREG_COUNT * 2 <= sizeof(unsigned char) * BITS_PER_BYTE);
+ assert(idx < MAX_MULTIREG_COUNT);
unsigned bits = flags >> (idx * 2); // It doesn't matter that we possibly leave other high bits here.
GenTreeFlags spillFlags = GTF_EMPTY;
@@ -3715,8 +3715,8 @@ inline GenTreeFlags GetMultiRegSpillFlagsByIdx(MultiRegSpillFlags flags, unsigne
//
inline MultiRegSpillFlags SetMultiRegSpillFlagsByIdx(MultiRegSpillFlags oldFlags, GenTreeFlags flagsToSet, unsigned idx)
{
- static_assert_no_msg(MAX_RET_REG_COUNT * 2 <= sizeof(unsigned char) * BITS_PER_BYTE);
- assert(idx < MAX_RET_REG_COUNT);
+ static_assert_no_msg(MAX_MULTIREG_COUNT * 2 <= sizeof(unsigned char) * BITS_PER_BYTE);
+ assert(idx < MAX_MULTIREG_COUNT);
MultiRegSpillFlags newFlags = oldFlags;
unsigned bits = 0;
@@ -8057,7 +8057,7 @@ struct GenTreeCopyOrReload : public GenTreeUnOp
// State required to support copy/reload of a multi-reg call node.
// The first register is always given by GetRegNum().
//
- regNumberSmall gtOtherRegs[MAX_RET_REG_COUNT - 1];
+ regNumberSmall gtOtherRegs[MAX_MULTIREG_COUNT - 1];
#endif
//----------------------------------------------------------
@@ -8072,7 +8072,7 @@ struct GenTreeCopyOrReload : public GenTreeUnOp
void ClearOtherRegs()
{
#if FEATURE_MULTIREG_RET
- for (unsigned i = 0; i < MAX_RET_REG_COUNT - 1; ++i)
+ for (unsigned i = 0; i < MAX_MULTIREG_COUNT - 1; ++i)
{
gtOtherRegs[i] = REG_NA;
}
@@ -8090,7 +8090,7 @@ struct GenTreeCopyOrReload : public GenTreeUnOp
//
regNumber GetRegNumByIdx(unsigned idx) const
{
- assert(idx < MAX_RET_REG_COUNT);
+ assert(idx < MAX_MULTIREG_COUNT);
if (idx == 0)
{
@@ -8116,7 +8116,7 @@ struct GenTreeCopyOrReload : public GenTreeUnOp
//
void SetRegNumByIdx(regNumber reg, unsigned idx)
{
- assert(idx < MAX_RET_REG_COUNT);
+ assert(idx < MAX_MULTIREG_COUNT);
if (idx == 0)
{
@@ -8153,7 +8153,7 @@ struct GenTreeCopyOrReload : public GenTreeUnOp
assert(OperGet() == from->OperGet());
#ifdef UNIX_AMD64_ABI
- for (unsigned i = 0; i < MAX_RET_REG_COUNT - 1; ++i)
+ for (unsigned i = 0; i < MAX_MULTIREG_COUNT - 1; ++i)
{
gtOtherRegs[i] = from->gtOtherRegs[i];
}
@@ -8170,7 +8170,7 @@ struct GenTreeCopyOrReload : public GenTreeUnOp
// but for COPY or RELOAD there is only a valid register for the register positions
// that must be copied or reloaded.
//
- for (unsigned i = MAX_RET_REG_COUNT; i > 1; i--)
+ for (unsigned i = MAX_MULTIREG_COUNT; i > 1; i--)
{
if (gtOtherRegs[i - 2] != REG_NA)
{
@@ -9166,6 +9166,7 @@ inline var_types GenTree::GetRegTypeByIndex(int regIndex) const
#endif // !defined(TARGET_64BIT)
#endif // FEATURE_MULTIREG_RET
+#ifdef FEATURE_HW_INTRINSICS
if (OperIsHWIntrinsic())
{
assert(TypeGet() == TYP_STRUCT);
@@ -9182,9 +9183,10 @@ inline var_types GenTree::GetRegTypeByIndex(int regIndex) const
#elif defined(TARGET_XARCH)
// At this time, the only multi-reg HW intrinsics all return the type of their
// arguments. If this changes, we will need a way to record or determine this.
- return gtGetOp1()->TypeGet();
+ return AsHWIntrinsic()->Op(1)->TypeGet();
#endif
}
+#endif // FEATURE_HW_INTRINSICS
if (OperIsScalarLocal())
{
diff --git a/src/coreclr/jit/hwintrinsic.h b/src/coreclr/jit/hwintrinsic.h
index b1299df1c1f1c..f610e61e4f06f 100644
--- a/src/coreclr/jit/hwintrinsic.h
+++ b/src/coreclr/jit/hwintrinsic.h
@@ -790,6 +790,12 @@ struct HWIntrinsicInfo
return 2;
#endif
+#ifdef TARGET_XARCH
+ case NI_X86Base_DivRem:
+ case NI_X86Base_X64_DivRem:
+ return 2;
+#endif // TARGET_XARCH
+
default:
unreached();
}
diff --git a/src/coreclr/jit/hwintrinsiccodegenxarch.cpp b/src/coreclr/jit/hwintrinsiccodegenxarch.cpp
index cb114b2d19701..cd5f6aa29ed8d 100644
--- a/src/coreclr/jit/hwintrinsiccodegenxarch.cpp
+++ b/src/coreclr/jit/hwintrinsiccodegenxarch.cpp
@@ -1187,6 +1187,43 @@ void CodeGen::genX86BaseIntrinsic(GenTreeHWIntrinsic* node)
break;
}
+ case NI_X86Base_DivRem:
+ case NI_X86Base_X64_DivRem:
+ {
+ assert(node->GetOperandCount() == 3);
+
+ // SIMD base type is from signature and can distinguish signed and unsigned
+ var_types targetType = node->GetSimdBaseType();
+ GenTree* op1 = node->Op(1);
+ GenTree* op2 = node->Op(2);
+ GenTree* op3 = node->Op(3);
+ instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, targetType);
+
+ regNumber op1Reg = op1->GetRegNum();
+ regNumber op2Reg = op2->GetRegNum();
+ regNumber op3Reg = op3->GetRegNum();
+
+ emitAttr attr = emitTypeSize(targetType);
+ emitter* emit = GetEmitter();
+
+ // op1: EAX, op2: EDX, op3: free
+ assert(op1Reg != REG_EDX);
+ assert(op2Reg != REG_EAX);
+ if (op3->isUsedFromReg())
+ {
+ assert(op3Reg != REG_EDX);
+ assert(op3Reg != REG_EAX);
+ }
+
+ emit->emitIns_Mov(INS_mov, attr, REG_EAX, op1Reg, /* canSkip */ true);
+ emit->emitIns_Mov(INS_mov, attr, REG_EDX, op2Reg, /* canSkip */ true);
+
+ // emit the DIV/IDIV instruction
+ emit->emitInsBinary(ins, attr, node, op3);
+
+ break;
+ }
+
default:
unreached();
break;
diff --git a/src/coreclr/jit/hwintrinsiclistxarch.h b/src/coreclr/jit/hwintrinsiclistxarch.h
index 8d5c2d16a35cb..d95ba8f43ef69 100644
--- a/src/coreclr/jit/hwintrinsiclistxarch.h
+++ b/src/coreclr/jit/hwintrinsiclistxarch.h
@@ -238,6 +238,7 @@ HARDWARE_INTRINSIC(Vector256, Xor,
HARDWARE_INTRINSIC(X86Base, BitScanForward, 0, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_bsf, INS_bsf, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_NoFloatingPointUsed|HW_Flag_NoRMWSemantics)
HARDWARE_INTRINSIC(X86Base, BitScanReverse, 0, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_bsr, INS_bsr, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_NoFloatingPointUsed|HW_Flag_NoRMWSemantics)
HARDWARE_INTRINSIC(X86Base, Pause, 0, 0, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Special, HW_Flag_NoContainment|HW_Flag_NoRMWSemantics)
+HARDWARE_INTRINSIC(X86Base, DivRem, 0, 3, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_idiv, INS_div, INS_idiv, INS_div, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_NoFloatingPointUsed|HW_Flag_BaseTypeFromSecondArg|HW_Flag_MultiReg|HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen)
// ***************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
// ISA Function name SIMD size NumArg Instructions Category Flags
@@ -246,6 +247,7 @@ HARDWARE_INTRINSIC(X86Base, Pause,
// X86Base 64-bit-only Intrinsics
HARDWARE_INTRINSIC(X86Base_X64, BitScanForward, 0, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_bsf, INS_bsf, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_NoFloatingPointUsed|HW_Flag_NoRMWSemantics)
HARDWARE_INTRINSIC(X86Base_X64, BitScanReverse, 0, 1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_bsr, INS_bsr, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_NoFloatingPointUsed|HW_Flag_NoRMWSemantics)
+HARDWARE_INTRINSIC(X86Base_X64, DivRem, 0, 3, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_idiv, INS_div, INS_invalid, INS_invalid}, HW_Category_Scalar, HW_Flag_NoFloatingPointUsed|HW_Flag_BaseTypeFromSecondArg|HW_Flag_MultiReg|HW_Flag_SpecialImport|HW_Flag_SpecialCodeGen)
// ***************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
// ISA Function name SIMD size NumArg Instructions Category Flags
diff --git a/src/coreclr/jit/hwintrinsicxarch.cpp b/src/coreclr/jit/hwintrinsicxarch.cpp
index a253aedc39704..22303b4ad12e4 100644
--- a/src/coreclr/jit/hwintrinsicxarch.cpp
+++ b/src/coreclr/jit/hwintrinsicxarch.cpp
@@ -513,7 +513,6 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic,
}
var_types simdBaseType = TYP_UNKNOWN;
-
if (simdSize != 0)
{
simdBaseType = JitType2PreciseVarType(simdBaseJitType);
@@ -2366,6 +2365,28 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic,
break;
}
+ case NI_X86Base_DivRem:
+ case NI_X86Base_X64_DivRem:
+ {
+ assert(sig->numArgs == 3);
+ assert(HWIntrinsicInfo::IsMultiReg(intrinsic));
+ assert(retType == TYP_STRUCT);
+ assert(simdBaseJitType != CORINFO_TYPE_UNDEF);
+
+ op3 = impPopStack().val;
+ op2 = impPopStack().val;
+ op1 = impPopStack().val;
+
+ GenTreeHWIntrinsic* divRemIntrinsic = gtNewScalarHWIntrinsicNode(retType, op1, op2, op3, intrinsic);
+
+ // Store the type from signature into SIMD base type for convenience
+ divRemIntrinsic->SetSimdBaseJitType(simdBaseJitType);
+
+ retNode = impAssignMultiRegTypeToVar(divRemIntrinsic,
+ sig->retTypeSigClass DEBUGARG(CorInfoCallConvExtension::Managed));
+ break;
+ }
+
case NI_SSE_CompareScalarGreaterThan:
case NI_SSE_CompareScalarGreaterThanOrEqual:
case NI_SSE_CompareScalarNotGreaterThan:
diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp
index 5f67160555849..dc327ff6fd426 100644
--- a/src/coreclr/jit/importer.cpp
+++ b/src/coreclr/jit/importer.cpp
@@ -11009,16 +11009,19 @@ GenTree* Compiler::impAssignMultiRegTypeToVar(GenTree* op,
{
unsigned tmpNum = lvaGrabTemp(true DEBUGARG("Return value temp for multireg return"));
impAssignTempGen(tmpNum, op, hClass, CHECK_SPILL_ALL);
- GenTree* ret = gtNewLclvNode(tmpNum, lvaTable[tmpNum].lvType);
+
+ LclVarDsc* varDsc = lvaGetDesc(tmpNum);
+
+ // Set "lvIsMultiRegRet" to block promotion under "!lvaEnregMultiRegVars".
+ varDsc->lvIsMultiRegRet = true;
+
+ GenTreeLclVar* ret = gtNewLclvNode(tmpNum, varDsc->lvType);
// TODO-1stClassStructs: Handle constant propagation and CSE-ing of multireg returns.
- ret->gtFlags |= GTF_DONT_CSE;
+ ret->SetDoNotCSE();
assert(IsMultiRegReturnedType(hClass, callConv) || op->IsMultiRegNode());
- // Set "lvIsMultiRegRet" to block promotion under "!lvaEnregMultiRegVars".
- lvaTable[tmpNum].lvIsMultiRegRet = true;
-
return ret;
}
diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp
index b25165f3f38b0..ba36ca369b5e6 100644
--- a/src/coreclr/jit/lower.cpp
+++ b/src/coreclr/jit/lower.cpp
@@ -3448,7 +3448,7 @@ void Lowering::LowerRet(GenTreeUnOp* ret)
#if FEATURE_MULTIREG_RET
if (comp->compMethodReturnsMultiRegRetType() && retVal->OperIs(GT_LCL_VAR))
{
- CheckMultiRegLclVar(retVal->AsLclVar(), &comp->compRetTypeDesc);
+ CheckMultiRegLclVar(retVal->AsLclVar(), comp->compRetTypeDesc.GetReturnRegCount());
}
#endif // FEATURE_MULTIREG_RET
#ifdef DEBUG
@@ -3533,12 +3533,7 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore)
if (srcIsMultiReg)
{
- const ReturnTypeDesc* retTypeDesc = nullptr;
- if (src->OperIs(GT_CALL))
- {
- retTypeDesc = src->AsCall()->GetReturnTypeDesc();
- }
- CheckMultiRegLclVar(lclStore->AsLclVar(), retTypeDesc);
+ CheckMultiRegLclVar(lclStore->AsLclVar(), src->GetMultiRegCount(comp));
}
const var_types lclRegType = varDsc->GetRegisterType(lclStore);
@@ -6888,68 +6883,61 @@ bool Lowering::NodesAreEquivalentLeaves(GenTree* tree1, GenTree* tree2)
// remain a multi-reg.
//
// Arguments:
-// lclNode - the GT_LCL_VAR or GT_STORE_LCL_VAR node.
-// retTypeDesc - a return type descriptor either for a call source of a store of
-// the local, or for the GT_RETURN consumer of the local.
-//
-// Notes:
-// If retTypeDesc is non-null, this method will check that the fields are compatible.
-// Otherwise, it will only check that the lclVar is independently promoted
-// (i.e. it is marked lvPromoted and not lvDoNotEnregister).
+// lclNode - the GT_LCL_VAR or GT_STORE_LCL_VAR node.
+// registerCount - use register count for uses; source register count for stores.
//
-bool Lowering::CheckMultiRegLclVar(GenTreeLclVar* lclNode, const ReturnTypeDesc* retTypeDesc)
+bool Lowering::CheckMultiRegLclVar(GenTreeLclVar* lclNode, int registerCount)
{
- bool canEnregister = false;
-#if FEATURE_MULTIREG_RET
+ bool canEnregisterAsMultiReg = false;
+ bool canEnregisterAsSingleReg = false;
+
+#if FEATURE_MULTIREG_RET || defined(FEATURE_HW_INTRINSICS)
LclVarDsc* varDsc = comp->lvaGetDesc(lclNode->GetLclNum());
+ if (varDsc->lvDoNotEnregister)
+ {
+ assert(!lclNode->IsMultiReg());
+ return false;
+ }
+
if ((comp->lvaEnregMultiRegVars) && varDsc->lvPromoted)
{
// We can enregister if we have a promoted struct and all the fields' types match the ABI requirements.
// Note that we don't promote structs with explicit layout, so we don't need to check field offsets, and
// if we have multiple types packed into a single register, we won't have matching reg and field counts,
// so we can tolerate mismatches of integer size.
- if (varDsc->lvPromoted && (comp->lvaGetPromotionType(varDsc) == Compiler::PROMOTION_TYPE_INDEPENDENT))
+ if (comp->lvaGetPromotionType(varDsc) == Compiler::PROMOTION_TYPE_INDEPENDENT)
{
- // If we have no retTypeDesc, we only care that it is independently promoted.
- if (retTypeDesc == nullptr)
+ if (registerCount == varDsc->lvFieldCnt)
{
- canEnregister = true;
- }
- else
- {
- unsigned regCount = retTypeDesc->GetReturnRegCount();
-
- if (regCount == varDsc->lvFieldCnt)
- {
- canEnregister = true;
- }
+ canEnregisterAsMultiReg = true;
}
}
}
-#ifdef TARGET_XARCH
- // For local stores on XARCH we only handle mismatched src/dest register count for calls of SIMD type.
- // If the source was another lclVar similarly promoted, we would have broken it into multiple stores.
- if (lclNode->OperIs(GT_STORE_LCL_VAR) && varTypeIsStruct(lclNode->Data()) && !lclNode->Data()->OperIs(GT_CALL))
+ else
{
- canEnregister = false;
- }
+ canEnregisterAsSingleReg = varTypeIsSIMD(lclNode);
+#ifdef TARGET_XARCH
+ if (lclNode->OperIs(GT_STORE_LCL_VAR) && varTypeIsStruct(lclNode->Data()) && !lclNode->Data()->OperIs(GT_CALL))
+ {
+ canEnregisterAsSingleReg = false;
+ }
#endif // TARGET_XARCH
+ }
- if (canEnregister)
+ if (canEnregisterAsSingleReg || canEnregisterAsMultiReg)
{
- lclNode->SetMultiReg();
+ if (canEnregisterAsMultiReg)
+ {
+ lclNode->SetMultiReg();
+ }
}
else
{
- lclNode->ClearMultiReg();
- if (varDsc->lvPromoted && !varDsc->lvDoNotEnregister)
- {
- comp->lvaSetVarDoNotEnregister(lclNode->GetLclNum() DEBUGARG(DoNotEnregisterReason::BlockOp));
- }
+ comp->lvaSetVarDoNotEnregister(lclNode->GetLclNum() DEBUGARG(DoNotEnregisterReason::BlockOp));
}
-#endif
+#endif // FEATURE_MULTIREG_RET || defined(FEATURE_HW_INTRINSICS)
- return canEnregister;
+ return canEnregisterAsSingleReg || canEnregisterAsMultiReg;
}
//------------------------------------------------------------------------
diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h
index 23aef6fbaea2e..e1e30f8a675af 100644
--- a/src/coreclr/jit/lower.h
+++ b/src/coreclr/jit/lower.h
@@ -339,7 +339,7 @@ class Lowering final : public Phase
#endif
void WidenSIMD12IfNecessary(GenTreeLclVarCommon* node);
- bool CheckMultiRegLclVar(GenTreeLclVar* lclNode, const ReturnTypeDesc* retTypeDesc);
+ bool CheckMultiRegLclVar(GenTreeLclVar* lclNode, int registerCount);
void LowerStoreLoc(GenTreeLclVarCommon* tree);
GenTree* LowerArrElem(GenTreeArrElem* arrElem);
void LowerRotate(GenTree* tree);
diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp
index 06fe0feb6019f..df2d4ea3a78c6 100644
--- a/src/coreclr/jit/lowerxarch.cpp
+++ b/src/coreclr/jit/lowerxarch.cpp
@@ -7354,6 +7354,20 @@ void Lowering::ContainCheckHWIntrinsic(GenTreeHWIntrinsic* node)
}
break;
}
+ case NI_X86Base_DivRem:
+ case NI_X86Base_X64_DivRem:
+ {
+ // DIV only allows divisor (op3) in memory
+ if (IsContainableHWIntrinsicOp(node, op3, &supportsRegOptional))
+ {
+ MakeSrcContained(node, op3);
+ }
+ else if (supportsRegOptional)
+ {
+ MakeSrcRegOptional(node, op3);
+ }
+ break;
+ }
default:
{
diff --git a/src/coreclr/jit/lsra.h b/src/coreclr/jit/lsra.h
index c63d0755ba4b1..888ca543d3f6a 100644
--- a/src/coreclr/jit/lsra.h
+++ b/src/coreclr/jit/lsra.h
@@ -1866,7 +1866,11 @@ class LinearScan : public LinearScanInterface
int BuildSimple(GenTree* tree);
int BuildOperandUses(GenTree* node, regMaskTP candidates = RBM_NONE);
- int BuildDelayFreeUses(GenTree* node, GenTree* rmwNode = nullptr, regMaskTP candidates = RBM_NONE);
+ void AddDelayFreeUses(RefPosition* refPosition, GenTree* rmwNode);
+ int BuildDelayFreeUses(GenTree* node,
+ GenTree* rmwNode = nullptr,
+ regMaskTP candidates = RBM_NONE,
+ RefPosition** useRefPosition = nullptr);
int BuildIndirUses(GenTreeIndir* indirTree, regMaskTP candidates = RBM_NONE);
int BuildAddrUses(GenTree* addr, regMaskTP candidates = RBM_NONE);
void HandleFloatVarArgs(GenTreeCall* call, GenTree* argNode, bool* callHasFloatRegArgs);
@@ -2262,9 +2266,9 @@ class RefPosition
// Used by RefTypeDef/Use positions of a multi-reg call node.
// Indicates the position of the register that this ref position refers to.
- // The max bits needed is based on max value of MAX_RET_REG_COUNT value
+ // The max bits needed is based on max value of MAX_MULTIREG_COUNT value
// across all targets and that happened to be 4 on Arm. Hence index value
- // would be 0..MAX_RET_REG_COUNT-1.
+ // would be 0..MAX_MULTIREG_COUNT-1.
unsigned char multiRegIdx : 2;
// Last Use - this may be true for multiple RefPositions in the same Interval
diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp
index 3908f1998792a..e8638c7d15eb0 100644
--- a/src/coreclr/jit/lsrabuild.cpp
+++ b/src/coreclr/jit/lsrabuild.cpp
@@ -3257,23 +3257,20 @@ void LinearScan::setDelayFree(RefPosition* use)
}
//------------------------------------------------------------------------
-// BuildDelayFreeUses: Build Use RefPositions for an operand that might be contained,
-// and which may need to be marked delayRegFree
+// AddDelayFreeUses: Mark useRefPosition as delay-free, if applicable, for the
+// rmw node.
//
// Arguments:
-// node - The node of interest
-// rmwNode - The node that has RMW semantics (if applicable)
-// candidates - The set of candidates for the uses
-//
-// Return Value:
-// The number of source registers used by the *parent* of this node.
+// useRefPosition - The use refposition that need to be delay-freed.
+// rmwNode - The node that has RMW semantics (if applicable)
//
-int LinearScan::BuildDelayFreeUses(GenTree* node, GenTree* rmwNode, regMaskTP candidates)
+void LinearScan::AddDelayFreeUses(RefPosition* useRefPosition, GenTree* rmwNode)
{
- RefPosition* use = nullptr;
- Interval* rmwInterval = nullptr;
- bool rmwIsLastUse = false;
- GenTree* addr = nullptr;
+ assert(useRefPosition != nullptr);
+
+ Interval* rmwInterval = nullptr;
+ bool rmwIsLastUse = false;
+ GenTree* addr = nullptr;
if ((rmwNode != nullptr) && isCandidateLocalRef(rmwNode))
{
rmwInterval = getIntervalForLocalVarNode(rmwNode->AsLclVar());
@@ -3282,6 +3279,41 @@ int LinearScan::BuildDelayFreeUses(GenTree* node, GenTree* rmwNode, regMaskTP ca
assert(!rmwNode->AsLclVar()->IsMultiReg());
rmwIsLastUse = rmwNode->AsLclVar()->IsLastUse(0);
}
+ // If node != rmwNode, then definitely node should be marked as "delayFree".
+ // However, if node == rmwNode, then we can mark node as "delayFree" only if
+ // none of the node/rmwNode are the last uses. If either of them are last use,
+ // we can safely reuse the rmwNode as destination.
+ if ((useRefPosition->getInterval() != rmwInterval) || (!rmwIsLastUse && !useRefPosition->lastUse))
+ {
+ setDelayFree(useRefPosition);
+ }
+}
+
+//------------------------------------------------------------------------
+// BuildDelayFreeUses: Build Use RefPositions for an operand that might be contained,
+// and which may need to be marked delayRegFree
+//
+// Arguments:
+// node - The node of interest
+// rmwNode - The node that has RMW semantics (if applicable)
+// candidates - The set of candidates for the uses
+// useRefPositionRef - If a use refposition is created, returns it. If none created, sets it to nullptr.
+//
+// Return Value:
+// The number of source registers used by the *parent* of this node.
+//
+int LinearScan::BuildDelayFreeUses(GenTree* node,
+ GenTree* rmwNode,
+ regMaskTP candidates,
+ RefPosition** useRefPositionRef)
+{
+ RefPosition* use = nullptr;
+ GenTree* addr = nullptr;
+ if (useRefPositionRef != nullptr)
+ {
+ *useRefPositionRef = nullptr;
+ }
+
if (!node->isContained())
{
use = BuildUse(node, candidates);
@@ -3320,14 +3352,7 @@ int LinearScan::BuildDelayFreeUses(GenTree* node, GenTree* rmwNode, regMaskTP ca
}
if (use != nullptr)
{
- // If node != rmwNode, then definitely node should be marked as "delayFree".
- // However, if node == rmwNode, then we can mark node as "delayFree" only if
- // none of the node/rmwNode are the last uses. If either of them are last use,
- // we can safely reuse the rmwNode as destination.
- if ((use->getInterval() != rmwInterval) || (!rmwIsLastUse && !use->lastUse))
- {
- setDelayFree(use);
- }
+ AddDelayFreeUses(use, rmwNode);
return 1;
}
@@ -3339,21 +3364,22 @@ int LinearScan::BuildDelayFreeUses(GenTree* node, GenTree* rmwNode, regMaskTP ca
if ((addrMode->Base() != nullptr) && !addrMode->Base()->isContained())
{
use = BuildUse(addrMode->Base(), candidates);
- if ((use->getInterval() != rmwInterval) || (!rmwIsLastUse && !use->lastUse))
- {
- setDelayFree(use);
- }
+ AddDelayFreeUses(use, rmwNode);
+
srcCount++;
}
if ((addrMode->Index() != nullptr) && !addrMode->Index()->isContained())
{
use = BuildUse(addrMode->Index(), candidates);
- if ((use->getInterval() != rmwInterval) || (!rmwIsLastUse && !use->lastUse))
- {
- setDelayFree(use);
- }
+ AddDelayFreeUses(use, rmwNode);
+
srcCount++;
}
+
+ if (useRefPositionRef != nullptr)
+ {
+ *useRefPositionRef = use;
+ }
return srcCount;
}
diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp
index 524bbc8577e96..3b9b11def2cb5 100644
--- a/src/coreclr/jit/lsraxarch.cpp
+++ b/src/coreclr/jit/lsraxarch.cpp
@@ -1967,7 +1967,23 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree, int* pDstCou
}
int srcCount = 0;
- int dstCount = intrinsicTree->IsValue() ? 1 : 0;
+ int dstCount;
+
+ if (intrinsicTree->IsValue())
+ {
+ if (HWIntrinsicInfo::IsMultiReg(intrinsicId))
+ {
+ dstCount = HWIntrinsicInfo::GetMultiRegCount(intrinsicId);
+ }
+ else
+ {
+ dstCount = 1;
+ }
+ }
+ else
+ {
+ dstCount = 0;
+ }
regMaskTP dstCandidates = RBM_NONE;
@@ -2152,6 +2168,45 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree, int* pDstCou
}
#endif // TARGET_X86
+ case NI_X86Base_DivRem:
+ case NI_X86Base_X64_DivRem:
+ {
+ assert(numArgs == 3);
+ assert(dstCount == 2);
+ assert(isRMW);
+
+ // DIV implicitly put op1(lower) to EAX and op2(upper) to EDX
+ srcCount += BuildOperandUses(op1, RBM_EAX);
+ srcCount += BuildOperandUses(op2, RBM_EDX);
+
+ if (!op3->isContained())
+ {
+ // For non-contained nodes, we want to make sure we delay free the register for
+ // op3 with respect to both op1 and op2. In other words, op3 shouldn't get same
+ // register that is assigned to either of op1 and op2.
+
+ RefPosition* op3RefPosition;
+ srcCount += BuildDelayFreeUses(op3, op1, RBM_NONE, &op3RefPosition);
+ if ((op3RefPosition != nullptr) && !op3RefPosition->delayRegFree)
+ {
+ // If op3 was not marked as delay-free for op1, mark it as delay-free
+ // if needed for op2.
+ AddDelayFreeUses(op3RefPosition, op2);
+ }
+ }
+ else
+ {
+ srcCount += BuildOperandUses(op3);
+ }
+
+ // result put in EAX and EDX
+ BuildDef(intrinsicTree, RBM_EAX, 0);
+ BuildDef(intrinsicTree, RBM_EDX, 1);
+
+ buildUses = false;
+ break;
+ }
+
case NI_BMI2_MultiplyNoFlags:
case NI_BMI2_X64_MultiplyNoFlags:
{
@@ -2422,7 +2477,9 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree, int* pDstCou
}
else
{
- assert(dstCount == 0);
+ // Currently dstCount = 2 is only used for DivRem, which has special constriants and handled above
+ assert((dstCount == 0) ||
+ ((dstCount == 2) && ((intrinsicId == NI_X86Base_DivRem) || (intrinsicId == NI_X86Base_X64_DivRem))));
}
*pDstCount = dstCount;
diff --git a/src/libraries/System.Private.CoreLib/ref/System.Private.CoreLib.ExtraApis.cs b/src/libraries/System.Private.CoreLib/ref/System.Private.CoreLib.ExtraApis.cs
index 84e99da5aa050..5465d3601a7b7 100644
--- a/src/libraries/System.Private.CoreLib/ref/System.Private.CoreLib.ExtraApis.cs
+++ b/src/libraries/System.Private.CoreLib/ref/System.Private.CoreLib.ExtraApis.cs
@@ -37,3 +37,19 @@ public static partial class Debug
public static System.Diagnostics.DebugProvider SetProvider(System.Diagnostics.DebugProvider provider) { throw null; }
}
}
+namespace System.Runtime.Intrinsics.X86
+{
+ public abstract partial class X86Base
+ {
+ public abstract partial class X64
+ {
+ public static (ulong Quotient, ulong Remainder) DivRem(ulong lower, ulong upper, ulong divisor) { throw null; }
+ public static (long Quotient, long Remainder) DivRem(ulong lower, long upper, long divisor) { throw null; }
+ }
+
+ public static (uint Quotient, uint Remainder) DivRem(uint lower, uint upper, uint divisor) { throw null; }
+ public static (int Quotient, int Remainder) DivRem(uint lower, int upper, int divisor) { throw null; }
+ public static (nuint Quotient, nuint Remainder) DivRem(nuint lower, nuint upper, nuint divisor) { throw null; }
+ public static (nint Quotient, nint Remainder) DivRem(nuint lower, nint upper, nint divisor) { throw null; }
+ }
+}
\ No newline at end of file
diff --git a/src/libraries/System.Private.CoreLib/ref/System.Private.CoreLib.ExtraApis.txt b/src/libraries/System.Private.CoreLib/ref/System.Private.CoreLib.ExtraApis.txt
index 0babd819e25d0..ef258ffce6cdd 100644
--- a/src/libraries/System.Private.CoreLib/ref/System.Private.CoreLib.ExtraApis.txt
+++ b/src/libraries/System.Private.CoreLib/ref/System.Private.CoreLib.ExtraApis.txt
@@ -5,3 +5,9 @@ T:System.Runtime.Serialization.DeserializationToken
M:System.Runtime.Serialization.SerializationInfo.StartDeserialization
T:System.Diagnostics.DebugProvider
M:System.Diagnostics.Debug.SetProvider(System.Diagnostics.DebugProvider)
+M:System.Runtime.Intrinsics.X86.X86Base.X64.DivRem(System.UInt64 lower, System.UInt64 upper, System.UInt64 divisor)
+M:System.Runtime.Intrinsics.X86.X86Base.X64.DivRem(System.UInt64 lower, System.Int64 upper, System.Int64 divisor)
+M:System.Runtime.Intrinsics.X86.X86Base.DivRem(uint lower, uint upper, uint divisor)
+M:System.Runtime.Intrinsics.X86.X86Base.DivRem(int lower, int upper, int divisor)
+M:System.Runtime.Intrinsics.X86.X86Base.DivRem(nuint lower, nuint upper, nuint divisor)
+M:System.Runtime.Intrinsics.X86.X86Base.DivRem(nuint lower, nint upper, nint divisor)
\ No newline at end of file
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.PlatformNotSupported.cs
index ad93f26912ee1..ba25f79678146 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.PlatformNotSupported.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.PlatformNotSupported.cs
@@ -9,6 +9,7 @@ namespace System.Runtime.Intrinsics.X86
///
/// This class provides access to the x86 base hardware instructions via intrinsics
///
+ [CLSCompliant(false)]
public abstract partial class X86Base
{
internal X86Base() { }
@@ -44,6 +45,18 @@ internal X64() { }
/// Its functionality is exposed in the public class.
///
internal static ulong BitScanReverse(ulong value) { throw new PlatformNotSupportedException(); }
+
+ ///
+ /// unsigned __int64 _udiv128(unsigned __int64 highdividend, unsigned __int64 lowdividend, unsigned __int64 divisor, unsigned __int64* remainder)
+ /// DIV reg/m64
+ ///
+ public static (ulong Quotient, ulong Remainder) DivRem(ulong lower, ulong upper, ulong divisor) { throw new PlatformNotSupportedException(); }
+
+ ///
+ /// __int64 _div128(__int64 highdividend, __int64 lowdividend, __int64 divisor, __int64* remainder)
+ /// DIV reg/m64
+ ///
+ public static (long Quotient, long Remainder) DivRem(ulong lower, long upper, long divisor) { throw new PlatformNotSupportedException(); }
}
///
@@ -74,6 +87,26 @@ internal X64() { }
///
public static (int Eax, int Ebx, int Ecx, int Edx) CpuId(int functionId, int subFunctionId) { throw new PlatformNotSupportedException(); }
+ ///
+ /// DIV reg/m32
+ ///
+ public static (uint Quotient, uint Remainder) DivRem(uint lower, uint upper, uint divisor) { throw new PlatformNotSupportedException(); }
+
+ ///
+ /// IDIV reg/m32
+ ///
+ public static (int Quotient, int Remainder) DivRem(uint lower, int upper, int divisor) { throw new PlatformNotSupportedException(); }
+
+ ///
+ /// IDIV reg/m
+ ///
+ public static (nuint Quotient, nuint Remainder) DivRem(nuint lower, nuint upper, nuint divisor) { throw new PlatformNotSupportedException(); }
+
+ ///
+ /// IDIV reg/m
+ ///
+ public static (nint Quotient, nint Remainder) DivRem(nuint lower, nint upper, nint divisor) { throw new PlatformNotSupportedException(); }
+
///
/// void _mm_pause (void);
/// PAUSE
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.cs
index c8b230b86166b..6ba4107549e42 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/X86Base.cs
@@ -10,6 +10,9 @@ namespace System.Runtime.Intrinsics.X86
/// This class provides access to the x86 base hardware instructions via intrinsics
///
[Intrinsic]
+#if SYSTEM_PRIVATE_CORELIB
+ [CLSCompliant(false)]
+#endif
public abstract partial class X86Base
{
internal X86Base() { }
@@ -44,6 +47,18 @@ internal X64() { }
/// Its functionality is exposed in the public class.
///
internal static ulong BitScanReverse(ulong value) => BitScanReverse(value);
+
+ ///
+ /// unsigned __int64 _udiv128(unsigned __int64 highdividend, unsigned __int64 lowdividend, unsigned __int64 divisor, unsigned __int64* remainder)
+ /// DIV reg/m64
+ ///
+ public static (ulong Quotient, ulong Remainder) DivRem(ulong lower, ulong upper, ulong divisor) => DivRem(lower, upper, divisor);
+
+ ///
+ /// __int64 _div128(__int64 highdividend, __int64 lowdividend, __int64 divisor, __int64* remainder)
+ /// DIV reg/m64
+ ///
+ public static (long Quotient, long Remainder) DivRem(ulong lower, long upper, long divisor) => DivRem(lower, upper, divisor);
}
///
@@ -74,11 +89,37 @@ internal X64() { }
///
public static unsafe (int Eax, int Ebx, int Ecx, int Edx) CpuId(int functionId, int subFunctionId)
{
+#if SYSTEM_PRIVATE_CORELIB
int* cpuInfo = stackalloc int[4];
__cpuidex(cpuInfo, functionId, subFunctionId);
return (cpuInfo[0], cpuInfo[1], cpuInfo[2], cpuInfo[3]);
+#else
+ return (0, 0, 0, 0);
+#endif
}
+ ///
+ /// unsigned _udiv64(unsigned __int64 dividend, unsigned divisor, unsigned* remainder)
+ /// DIV reg/m32
+ ///
+ public static (uint Quotient, uint Remainder) DivRem(uint lower, uint upper, uint divisor) => DivRem(lower, upper, divisor);
+
+ ///
+ /// int _div64(__int64 dividend, int divisor, int* remainder)
+ /// IDIV reg/m32
+ ///
+ public static (int Quotient, int Remainder) DivRem(uint lower, int upper, int divisor) => DivRem(lower, upper, divisor);
+
+ ///
+ /// IDIV reg/m
+ ///
+ public static (nuint Quotient, nuint Remainder) DivRem(nuint lower, nuint upper, nuint divisor) => DivRem(lower, upper, divisor);
+
+ ///
+ /// IDIV reg/m
+ ///
+ public static (nint Quotient, nint Remainder) DivRem(nuint lower, nint upper, nint divisor) => DivRem(lower, upper, divisor);
+
///
/// void _mm_pause (void);
/// PAUSE
diff --git a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs
index fec48c2c5c1be..47885edc44bda 100644
--- a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs
+++ b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs
@@ -5210,6 +5210,8 @@ internal X64() { }
public static new bool IsSupported { get { throw null; } }
}
}
+
+ [System.CLSCompliantAttribute(false)]
public abstract partial class X86Base
{
internal X86Base() { }
diff --git a/src/libraries/System.Runtime.Intrinsics/src/CompatibilitySuppressions.xml b/src/libraries/System.Runtime.Intrinsics/src/CompatibilitySuppressions.xml
new file mode 100644
index 0000000000000..3f4ec97bc8d07
--- /dev/null
+++ b/src/libraries/System.Runtime.Intrinsics/src/CompatibilitySuppressions.xml
@@ -0,0 +1,76 @@
+
+
+
+
+ CP0002
+ M:System.Runtime.Intrinsics.X86.X86Base.DivRem(nuint lower, nint upper, nint divisor)
+ ref/net8.0/System.Runtime.Intrinsics.dll
+ lib/net8.0/System.Runtime.Intrinsics.dll
+
+
+ CP0002
+ M:System.Runtime.Intrinsics.X86.X86Base.DivRem(nuint lower, nuint upper, nuint divisor)
+ ref/net8.0/System.Runtime.Intrinsics.dll
+ lib/net8.0/System.Runtime.Intrinsics.dll
+
+
+ CP0002
+ M:System.Runtime.Intrinsics.X86.X86Base.DivRem(System.UInt32 lower, System.Int32 upper, System.Int32 divisor)
+ ref/net8.0/System.Runtime.Intrinsics.dll
+ lib/net8.0/System.Runtime.Intrinsics.dll
+
+
+ CP0002
+ M:System.Runtime.Intrinsics.X86.X86Base.DivRem(System.UInt32 lower, System.UInt32 upper, System.UInt32 divisor)
+ ref/net8.0/System.Runtime.Intrinsics.dll
+ lib/net8.0/System.Runtime.Intrinsics.dll
+
+
+ CP0002
+ M:System.Runtime.Intrinsics.X86.X86Base.DivRem(System.UInt32,System.Int32,System.Int32)
+ ref/net8.0/System.Runtime.Intrinsics.dll
+ lib/net8.0/System.Runtime.Intrinsics.dll
+
+
+ CP0002
+ M:System.Runtime.Intrinsics.X86.X86Base.DivRem(System.UInt32,System.UInt32,System.UInt32)
+ ref/net8.0/System.Runtime.Intrinsics.dll
+ lib/net8.0/System.Runtime.Intrinsics.dll
+
+
+ CP0002
+ M:System.Runtime.Intrinsics.X86.X86Base.DivRem(System.UIntPtr,System.IntPtr,System.IntPtr)
+ ref/net8.0/System.Runtime.Intrinsics.dll
+ lib/net8.0/System.Runtime.Intrinsics.dll
+
+
+ CP0002
+ M:System.Runtime.Intrinsics.X86.X86Base.DivRem(System.UIntPtr,System.UIntPtr,System.UIntPtr)
+ ref/net8.0/System.Runtime.Intrinsics.dll
+ lib/net8.0/System.Runtime.Intrinsics.dll
+
+
+ CP0002
+ M:System.Runtime.Intrinsics.X86.X86Base.X64.DivRem(System.UInt64 lower, System.Int64 upper, System.Int64 divisor)
+ ref/net8.0/System.Runtime.Intrinsics.dll
+ lib/net8.0/System.Runtime.Intrinsics.dll
+
+
+ CP0002
+ M:System.Runtime.Intrinsics.X86.X86Base.X64.DivRem(System.UInt64 lower, System.UInt64 upper, System.UInt64 divisor)
+ ref/net8.0/System.Runtime.Intrinsics.dll
+ lib/net8.0/System.Runtime.Intrinsics.dll
+
+
+ CP0002
+ M:System.Runtime.Intrinsics.X86.X86Base.X64.DivRem(System.UInt64,System.Int64,System.Int64)
+ ref/net8.0/System.Runtime.Intrinsics.dll
+ lib/net8.0/System.Runtime.Intrinsics.dll
+
+
+ CP0002
+ M:System.Runtime.Intrinsics.X86.X86Base.X64.DivRem(System.UInt64,System.UInt64,System.UInt64)
+ ref/net8.0/System.Runtime.Intrinsics.dll
+ lib/net8.0/System.Runtime.Intrinsics.dll
+
+
\ No newline at end of file
diff --git a/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_X86.cs b/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_X86.cs
index c20fca73cef70..52f7d523ea61d 100644
--- a/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_X86.cs
+++ b/src/tests/Common/GenerateHWIntrinsicTests/GenerateHWIntrinsicTests_X86.cs
@@ -1264,6 +1264,20 @@
("ScalarTernOpBinResTest.template", new Dictionary { ["Isa"] = "Bmi2.X64", ["Method"] = "MultiplyNoFlags", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt64", ["Op3BaseType"] = "UInt64", ["NextValueOp1"] = "UInt64.MaxValue", ["NextValueOp2"] = "UInt64.MaxValue", ["NextValueOp3"] = "0", ["ValidateResult"] = "ulong expectedHigher = 18446744073709551614, expectedLower = 1; isUnexpectedResult = (expectedHigher != higher) || (expectedLower != lower);" }),
};
+(string templateFileName, Dictionary templateData)[] X86BaseInputs = new []
+{
+ ("ScalarTernOpTupleBinRetTest.template", new Dictionary { ["Isa"] = "X86Base", ["Method"] = "DivRem", ["RetBaseType"] = "Int32", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "Int32", ["Op3BaseType"] = "Int32", ["NextValueOp1"] = "UInt32.MaxValue", ["NextValueOp2"] = "-2", ["NextValueOp3"] = "-0x10001", ["ValidateResult"] = " int expectedQuotient = 0xFFFF; int expectedReminder = -2; isUnexpectedResult = (expectedQuotient != ret1) || (expectedReminder != ret2);" }),
+ ("ScalarTernOpTupleBinRetTest.template", new Dictionary { ["Isa"] = "X86Base", ["Method"] = "DivRem", ["RetBaseType"] = "UInt32", ["Op1BaseType"] = "UInt32", ["Op2BaseType"] = "UInt32", ["Op3BaseType"] = "UInt32", ["NextValueOp1"] = "1", ["NextValueOp2"] = "1", ["NextValueOp3"] = "0x80000000", ["ValidateResult"] = "uint expectedQuotient = 2; uint expectedReminder = 1; isUnexpectedResult = (expectedQuotient != ret1) || (expectedReminder != ret2);" }),
+ ("ScalarTernOpTupleBinRetTest.template", new Dictionary { ["Isa"] = "X86Base", ["Method"] = "DivRem", ["RetBaseType"] = "nint", ["Op1BaseType"] = "nuint", ["Op2BaseType"] = "nint", ["Op3BaseType"] = "nint", ["NextValueOp1"] = "nuint.MaxValue", ["NextValueOp2"] = "-2", ["NextValueOp3"] = "-((nint)1 << (IntPtr.Size * 4)) - 1", ["ValidateResult"] = " nint expectedQuotient = ((nint)1 << (IntPtr.Size * 4)) - 1; nint expectedReminder = -2; isUnexpectedResult = (expectedQuotient != ret1) || (expectedReminder != ret2);" }),
+ ("ScalarTernOpTupleBinRetTest.template", new Dictionary { ["Isa"] = "X86Base", ["Method"] = "DivRem", ["RetBaseType"] = "nuint", ["Op1BaseType"] = "nuint", ["Op2BaseType"] = "nuint", ["Op3BaseType"] = "nuint", ["NextValueOp1"] = "1", ["NextValueOp2"] = "1", ["NextValueOp3"] = "(nuint)nint.MinValue", ["ValidateResult"] = "nuint expectedQuotient = 2; nuint expectedReminder = 1; isUnexpectedResult = (expectedQuotient != ret1) || (expectedReminder != ret2);" }),
+};
+
+(string templateFileName, Dictionary templateData)[] X86BaseX64Inputs = new []
+{
+ ("ScalarTernOpTupleBinRetTest.template", new Dictionary { ["Isa"] = "X86Base.X64", ["Method"] = "DivRem", ["RetBaseType"] = "Int64", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "Int64", ["Op3BaseType"] = "Int64", ["NextValueOp1"] = "UInt64.MaxValue", ["NextValueOp2"] = "-2", ["NextValueOp3"] = "-0x100000001", ["ValidateResult"] = " long expectedQuotient = 0xFFFFFFFF; long expectedReminder = -2; isUnexpectedResult = (expectedQuotient != ret1) || (expectedReminder != ret2);" }),
+ ("ScalarTernOpTupleBinRetTest.template", new Dictionary { ["Isa"] = "X86Base.X64", ["Method"] = "DivRem", ["RetBaseType"] = "UInt64", ["Op1BaseType"] = "UInt64", ["Op2BaseType"] = "UInt64", ["Op3BaseType"] = "UInt64", ["NextValueOp1"] = "1", ["NextValueOp2"] = "1", ["NextValueOp3"] = "0x8000000000000000", ["ValidateResult"] = "ulong expectedQuotient = 2; ulong expectedReminder = 1; isUnexpectedResult = (expectedQuotient != ret1) || (expectedReminder != ret2);" }),
+};
+
Dictionary extraHelperFiles = new Dictionary
{
["Sse2Verify"] = @"..\Sse2\Sse2Verify.cs",
@@ -1374,6 +1388,11 @@ void ProcessInput(StreamWriter testListFile, string groupName, (string templateF
testName += ".BinRes";
suffix += "BinRes";
}
+ else if (input.templateFileName == "ScalarTernOpTupleBinRetTest.template")
+ {
+ testName += ".Tuple3Op";
+ suffix += "Tuple3Op";
+ }
var fileName = Path.Combine(outputDirectory, $"{testName}.cs");
@@ -1414,3 +1433,5 @@ void ProcessInput(StreamWriter testListFile, string groupName, (string templateF
File.WriteAllText(fileName, template);
}
+ProcessInputs("X86Base", X86BaseInputs);
+ProcessInputs("X86Base.X64", X86BaseX64Inputs);
diff --git a/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_r.csproj b/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_r.csproj
index d81af3a381450..9210387885675 100644
--- a/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_r.csproj
+++ b/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_r.csproj
@@ -15,7 +15,7 @@
-
+
diff --git a/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_ro.csproj b/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_ro.csproj
index cec6dbb86c481..36d5b5608fede 100644
--- a/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_ro.csproj
+++ b/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_ro.csproj
@@ -16,7 +16,7 @@
-
+
diff --git a/src/tests/JIT/HardwareIntrinsics/X86/Shared/ScalarTernOpTupleBinRetTest.template b/src/tests/JIT/HardwareIntrinsics/X86/Shared/ScalarTernOpTupleBinRetTest.template
new file mode 100644
index 0000000000000..e1e04ad6913cd
--- /dev/null
+++ b/src/tests/JIT/HardwareIntrinsics/X86/Shared/ScalarTernOpTupleBinRetTest.template
@@ -0,0 +1,257 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+/******************************************************************************
+ * This file is auto-generated from a template file by the GenerateTests.csx *
+ * script in tests\src\JIT\HardwareIntrinsics\X86\Shared. In order to make *
+ * changes, please update the corresponding template and run according to the *
+ * directions listed in the file. *
+ ******************************************************************************/
+extern alias CoreLib;
+using X86Base = CoreLib::System.Runtime.Intrinsics.X86.X86Base;
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace JIT.HardwareIntrinsics.X86
+{
+ public static partial class Program
+ {
+ private static void {Method}{RetBaseType}Tuple3Op()
+ {
+ var test = new ScalarTernOpTupleTest__{Method}{RetBaseType}();
+
+ if (test.IsSupported)
+ {
+ // Validates basic functionality works, using Unsafe.ReadUnaligned
+ test.RunBasicScenario_UnsafeRead();
+
+ // Validates calling via reflection works, using Unsafe.ReadUnaligned
+ test.RunReflectionScenario_UnsafeRead();
+
+ // Validates passing a static member works
+ test.RunClsVarScenario();
+
+ // Validates passing a local works, using Unsafe.ReadUnaligned
+ test.RunLclVarScenario_UnsafeRead();
+
+ // Validates passing the field of a local class works
+ test.RunClassLclFldScenario();
+
+ // Validates passing an instance member of a class works
+ test.RunClassFldScenario();
+
+ // Validates passing the field of a local struct works
+ test.RunStructLclFldScenario();
+
+ // Validates passing an instance member of a struct works
+ test.RunStructFldScenario();
+ }
+ else
+ {
+ // Validates we throw on unsupported hardware
+ test.RunUnsupportedScenario();
+ }
+
+ if (!test.Succeeded)
+ {
+ throw new Exception("One or more scenarios did not complete as expected.");
+ }
+ }
+ }
+
+ public sealed unsafe class ScalarTernOpTupleTest__{Method}{RetBaseType}
+ {
+ private struct TestStruct
+ {
+ public {Op1BaseType} _fld1;
+ public {Op2BaseType} _fld2;
+ public {Op3BaseType} _fld3;
+
+ public static TestStruct Create()
+ {
+ var testStruct = new TestStruct();
+
+ testStruct._fld1 = {NextValueOp1};
+ testStruct._fld2 = {NextValueOp2};
+ testStruct._fld3 = {NextValueOp3};
+
+ return testStruct;
+ }
+
+ public void RunStructFldScenario(ScalarTernOpTupleTest__{Method}{RetBaseType} testClass)
+ {
+ var result = {Isa}.{Method}(_fld1, _fld2, _fld3);
+ testClass.ValidateResult(_fld1, _fld2, _fld3, result);
+ }
+ }
+
+ private static {Op1BaseType} _data1;
+ private static {Op2BaseType} _data2;
+ private static {Op3BaseType} _data3;
+
+ private static {Op1BaseType} _clsVar1;
+ private static {Op2BaseType} _clsVar2;
+ private static {Op3BaseType} _clsVar3;
+
+ private {Op1BaseType} _fld1;
+ private {Op2BaseType} _fld2;
+ private {Op3BaseType} _fld3;
+
+ static ScalarTernOpTupleTest__{Method}{RetBaseType}()
+ {
+ _clsVar1 = {NextValueOp1};
+ _clsVar2 = {NextValueOp2};
+ _clsVar3 = {NextValueOp3};
+ }
+
+ public ScalarTernOpTupleTest__{Method}{RetBaseType}()
+ {
+ Succeeded = true;
+
+ _fld1 = {NextValueOp1};
+ _fld2 = {NextValueOp2};
+ _fld3 = {NextValueOp3};
+
+ _data1 = {NextValueOp1};
+ _data2 = {NextValueOp2};
+ _data3 = {NextValueOp3};
+ }
+
+ public bool IsSupported => {Isa}.IsSupported;
+
+ public bool Succeeded { get; set; }
+
+ public void RunBasicScenario_UnsafeRead()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunBasicScenario_UnsafeRead));
+
+ var result = {Isa}.{Method}(
+ Unsafe.ReadUnaligned<{Op1BaseType}>(ref Unsafe.As<{Op1BaseType}, byte>(ref _data1)),
+ Unsafe.ReadUnaligned<{Op2BaseType}>(ref Unsafe.As<{Op2BaseType}, byte>(ref _data2)),
+ Unsafe.ReadUnaligned<{Op3BaseType}>(ref Unsafe.As<{Op3BaseType}, byte>(ref _data3))
+ );
+
+ ValidateResult(_data1, _data2, _data3, result);
+ }
+
+ public void RunReflectionScenario_UnsafeRead()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunReflectionScenario_UnsafeRead));
+
+ var result = typeof({Isa}).GetMethod(nameof({Isa}.{Method}), new Type[] { typeof({Op1BaseType}), typeof({Op2BaseType}), typeof({Op3BaseType}) })
+ .Invoke(null, new object[] {
+ Unsafe.ReadUnaligned<{Op1BaseType}>(ref Unsafe.As<{Op1BaseType}, byte>(ref _data1)),
+ Unsafe.ReadUnaligned<{Op2BaseType}>(ref Unsafe.As<{Op2BaseType}, byte>(ref _data2)),
+ Unsafe.ReadUnaligned<{Op3BaseType}>(ref Unsafe.As<{Op3BaseType}, byte>(ref _data3))
+ });
+
+ ValidateResult(_data1, _data2, _data3, (({RetBaseType}, {RetBaseType}))result);
+ }
+
+ public void RunClsVarScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunClsVarScenario));
+
+ var result = {Isa}.{Method}(
+ _clsVar1,
+ _clsVar2,
+ _clsVar3
+ );
+
+ ValidateResult(_clsVar1, _clsVar2, _clsVar3, result);
+ }
+
+ public void RunLclVarScenario_UnsafeRead()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunLclVarScenario_UnsafeRead));
+
+ var data1 = Unsafe.ReadUnaligned<{Op1BaseType}>(ref Unsafe.As<{Op1BaseType}, byte>(ref _data1));
+ var data2 = Unsafe.ReadUnaligned<{Op2BaseType}>(ref Unsafe.As<{Op2BaseType}, byte>(ref _data2));
+ var data3 = Unsafe.ReadUnaligned<{Op3BaseType}>(ref Unsafe.As<{Op3BaseType}, byte>(ref _data3));
+ var result = {Isa}.{Method}(data1, data2, data3);
+
+ ValidateResult(data1, data2, data3, result);
+ }
+
+ public void RunClassLclFldScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunClassLclFldScenario));
+
+ var test = new ScalarTernOpTupleTest__{Method}{RetBaseType}();
+ var result = {Isa}.{Method}(test._fld1, test._fld2, test._fld3);
+
+ ValidateResult(test._fld1, test._fld2, test._fld3, result);
+ }
+
+ public void RunClassFldScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunClassFldScenario));
+
+ var result = {Isa}.{Method}(_fld1, _fld2, _fld3);
+ ValidateResult(_fld1, _fld2, _fld3, result);
+ }
+
+ public void RunStructLclFldScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunStructLclFldScenario));
+
+ var test = TestStruct.Create();
+ var result = {Isa}.{Method}(test._fld1, test._fld2, test._fld3);
+
+ ValidateResult(test._fld1, test._fld2, test._fld3, result);
+ }
+
+ public void RunStructFldScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunStructFldScenario));
+
+ var test = TestStruct.Create();
+ test.RunStructFldScenario(this);
+ }
+
+ public void RunUnsupportedScenario()
+ {
+ TestLibrary.TestFramework.BeginScenario(nameof(RunUnsupportedScenario));
+
+ bool succeeded = false;
+
+ try
+ {
+ RunBasicScenario_UnsafeRead();
+ }
+ catch (PlatformNotSupportedException)
+ {
+ succeeded = true;
+ }
+
+ if (!succeeded)
+ {
+ Succeeded = false;
+ }
+ }
+
+ private void ValidateResult({Op1BaseType} op1, {Op2BaseType} op2, {Op3BaseType} op3, ({RetBaseType}, {RetBaseType}) result, [CallerMemberName] string method = "")
+ {
+ (var ret1, var ret2) = result;
+ var isUnexpectedResult = false;
+
+ {ValidateResult}
+
+ if (isUnexpectedResult)
+ {
+ TestLibrary.TestFramework.LogInformation($"{nameof({Isa})}.{nameof({Isa}.{Method})}<({RetBaseType}, {RetBaseType})>({Op1BaseType}, {Op2BaseType}, {Op3BaseType}): {Method} failed:");
+ TestLibrary.TestFramework.LogInformation($" op1: {op1}");
+ TestLibrary.TestFramework.LogInformation($" op2: {op2}");
+ TestLibrary.TestFramework.LogInformation($" op3: {op3}");
+ TestLibrary.TestFramework.LogInformation($" result1: {ret1}");
+ TestLibrary.TestFramework.LogInformation($" result2: {ret2}");
+ TestLibrary.TestFramework.LogInformation(string.Empty);
+
+ Succeeded = false;
+ }
+ }
+ }
+}
diff --git a/src/tests/JIT/HardwareIntrinsics/X86/X86Base.X64/Program.X86Base.X64.cs b/src/tests/JIT/HardwareIntrinsics/X86/X86Base.X64/Program.X86Base.X64.cs
new file mode 100644
index 0000000000000..40dc13b975f47
--- /dev/null
+++ b/src/tests/JIT/HardwareIntrinsics/X86/X86Base.X64/Program.X86Base.X64.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+using System;
+using System.Collections.Generic;
+
+[assembly:Xunit.ActiveIssue("https://github.com/dotnet/runtime/issues/75767", typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMonoLLVMAOT))]
+[assembly:Xunit.ActiveIssue("https://github.com/dotnet/runtime/issues/75767", typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMonoLLVMFULLAOT))]
+namespace JIT.HardwareIntrinsics.X86._X86Base.X64
+{
+ public static partial class Program
+ {
+ static Program()
+ {
+
+ }
+ }
+}
diff --git a/src/tests/JIT/HardwareIntrinsics/X86/X86Base.X64/X86Base.X64_r.csproj b/src/tests/JIT/HardwareIntrinsics/X86/X86Base.X64/X86Base.X64_r.csproj
new file mode 100644
index 0000000000000..d4c72bc89146c
--- /dev/null
+++ b/src/tests/JIT/HardwareIntrinsics/X86/X86Base.X64/X86Base.X64_r.csproj
@@ -0,0 +1,18 @@
+
+
+ X86_X86Base.X64_r
+ false
+ true
+
+ true
+
+
+ Embedded
+
+
+
+
+
+
+
+
diff --git a/src/tests/JIT/HardwareIntrinsics/X86/X86Base.X64/X86Base.X64_ro.csproj b/src/tests/JIT/HardwareIntrinsics/X86/X86Base.X64/X86Base.X64_ro.csproj
new file mode 100644
index 0000000000000..e0a62fa9bc562
--- /dev/null
+++ b/src/tests/JIT/HardwareIntrinsics/X86/X86Base.X64/X86Base.X64_ro.csproj
@@ -0,0 +1,18 @@
+
+
+ X86_X86Base.X64_ro
+ false
+ true
+
+ true
+
+
+ Embedded
+ True
+
+
+
+
+
+
+
diff --git a/src/tests/JIT/HardwareIntrinsics/X86/X86Base/DivRem.RefOnly.csproj b/src/tests/JIT/HardwareIntrinsics/X86/X86Base/DivRem.RefOnly.csproj
new file mode 100644
index 0000000000000..1a70f52cd8874
--- /dev/null
+++ b/src/tests/JIT/HardwareIntrinsics/X86/X86Base/DivRem.RefOnly.csproj
@@ -0,0 +1,22 @@
+
+
+
+ true
+ Library
+ SharedLibrary
+ System.Private.CoreLib
+ 436
+ 436
+ true
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/tests/JIT/HardwareIntrinsics/X86/X86Base/Program.X86Base.cs b/src/tests/JIT/HardwareIntrinsics/X86/X86Base/Program.X86Base.cs
new file mode 100644
index 0000000000000..364a8aee4b047
--- /dev/null
+++ b/src/tests/JIT/HardwareIntrinsics/X86/X86Base/Program.X86Base.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+using System;
+using System.Collections.Generic;
+
+[assembly:Xunit.ActiveIssue("https://github.com/dotnet/runtime/issues/75767", typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMonoLLVMAOT))]
+[assembly:Xunit.ActiveIssue("https://github.com/dotnet/runtime/issues/75767", typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMonoLLVMFULLAOT))]
+namespace JIT.HardwareIntrinsics.X86._X86Base
+{
+ public static partial class Program
+ {
+ static Program()
+ {
+
+ }
+ }
+}
diff --git a/src/tests/JIT/HardwareIntrinsics/X86/X86Base/X86Base_r.csproj b/src/tests/JIT/HardwareIntrinsics/X86/X86Base/X86Base_r.csproj
new file mode 100644
index 0000000000000..f16999cb670d1
--- /dev/null
+++ b/src/tests/JIT/HardwareIntrinsics/X86/X86Base/X86Base_r.csproj
@@ -0,0 +1,18 @@
+
+
+ X86_X86Base_r
+ false
+ true
+
+ true
+
+
+ Embedded
+
+
+
+
+
+
+
+
diff --git a/src/tests/JIT/HardwareIntrinsics/X86/X86Base/X86Base_ro.csproj b/src/tests/JIT/HardwareIntrinsics/X86/X86Base/X86Base_ro.csproj
new file mode 100644
index 0000000000000..b09c5e72a5157
--- /dev/null
+++ b/src/tests/JIT/HardwareIntrinsics/X86/X86Base/X86Base_ro.csproj
@@ -0,0 +1,18 @@
+
+
+ X86_X86Base_ro
+ false
+ true
+
+ true
+
+
+ Embedded
+ True
+
+
+
+
+
+
+
diff --git a/src/tests/issues.targets b/src/tests/issues.targets
index 9420fdc0eec52..6704c64474293 100644
--- a/src/tests/issues.targets
+++ b/src/tests/issues.targets
@@ -1417,6 +1417,15 @@
https://github.com/dotnet/runtime/issues/54185
+
+ https://github.com/dotnet/runtime/issues/75767
+
+
+ https://github.com/dotnet/runtime/issues/75767
+
+
+ https://github.com/dotnet/runtime/issues/75767
+
Mono does not define out of range fp to int conversions
@@ -2840,6 +2849,15 @@
expected failure: unsupported type with ByRefLike parameters currently fails at AOT compile time, not runtime
+
+ https://github.com/dotnet/runtime/issues/75767
+
+
+ https://github.com/dotnet/runtime/issues/75767
+
+
+ https://github.com/dotnet/runtime/issues/75767
+
https://github.com/dotnet/runtime/issues/70490
@@ -3149,6 +3167,15 @@
https://github.com/dotnet/runtime/issues/73454;https://github.com/dotnet/runtime/pull/61707#issuecomment-973122341
+
+ https://github.com/dotnet/runtime/issues/75767
+
+
+ https://github.com/dotnet/runtime/issues/75767
+
+
+ https://github.com/dotnet/runtime/issues/75767
+
needs triage