diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index c0581ee7f7f162..22f170d00e0f59 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -690,16 +690,36 @@ void CodeGen::genIntrinsic(GenTreeIntrinsic* treeNode) break; case NI_System_Math_Max: + { genConsumeOperands(treeNode->AsOp()); GetEmitter()->emitIns_R_R_R(INS_fmax, emitActualTypeSize(treeNode), treeNode->GetRegNum(), srcNode->GetRegNum(), treeNode->gtGetOp2()->GetRegNum()); break; + } + + case NI_System_Math_MaxNumber: + { + genConsumeOperands(treeNode->AsOp()); + GetEmitter()->emitIns_R_R_R(INS_fmaxnm, emitActualTypeSize(treeNode), treeNode->GetRegNum(), + srcNode->GetRegNum(), treeNode->gtGetOp2()->GetRegNum()); + break; + } case NI_System_Math_Min: + { genConsumeOperands(treeNode->AsOp()); GetEmitter()->emitIns_R_R_R(INS_fmin, emitActualTypeSize(treeNode), treeNode->GetRegNum(), srcNode->GetRegNum(), treeNode->gtGetOp2()->GetRegNum()); break; + } + + case NI_System_Math_MinNumber: + { + genConsumeOperands(treeNode->AsOp()); + GetEmitter()->emitIns_R_R_R(INS_fminnm, emitActualTypeSize(treeNode), treeNode->GetRegNum(), + srcNode->GetRegNum(), treeNode->gtGetOp2()->GetRegNum()); + break; + } #endif // TARGET_ARM64 case NI_System_Math_Sqrt: diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index e8f293b595d08d..0d24478ae115d8 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3957,8 +3957,17 @@ class Compiler var_types callType, NamedIntrinsic intrinsicName, bool tailCall); + GenTree* impMinMaxIntrinsic(CORINFO_METHOD_HANDLE method, + CORINFO_SIG_INFO* sig, + CorInfoType callJitType, + NamedIntrinsic intrinsicName, + bool tailCall, + bool isMax, + bool isMagnitude, + bool isNumber); NamedIntrinsic lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method); - NamedIntrinsic lookupPrimitiveNamedIntrinsic(CORINFO_METHOD_HANDLE method, const char* methodName); + NamedIntrinsic lookupPrimitiveFloatNamedIntrinsic(CORINFO_METHOD_HANDLE method, const char* methodName); + NamedIntrinsic lookupPrimitiveIntNamedIntrinsic(CORINFO_METHOD_HANDLE method, const char* methodName); GenTree* impUnsupportedNamedIntrinsic(unsigned helper, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig, diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 17b2026fa64e86..d8b4e8790a31e6 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -5477,7 +5477,13 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) case NI_System_Math_Log2: case NI_System_Math_Log10: case NI_System_Math_Max: + case NI_System_Math_MaxMagnitude: + case NI_System_Math_MaxMagnitudeNumber: + case NI_System_Math_MaxNumber: case NI_System_Math_Min: + case NI_System_Math_MinMagnitude: + case NI_System_Math_MinMagnitudeNumber: + case NI_System_Math_MinNumber: case NI_System_Math_Pow: case NI_System_Math_Round: case NI_System_Math_Sin: @@ -5824,9 +5830,17 @@ unsigned Compiler::gtSetEvalOrder(GenTree* tree) break; case NI_System_Math_Max: + case NI_System_Math_MaxMagnitude: + case NI_System_Math_MaxMagnitudeNumber: + case NI_System_Math_MaxNumber: case NI_System_Math_Min: + case NI_System_Math_MinMagnitude: + case NI_System_Math_MinMagnitudeNumber: + case NI_System_Math_MinNumber: + { level++; break; + } default: assert(!"Unknown binary GT_INTRINSIC operator"); @@ -12235,9 +12249,27 @@ void Compiler::gtDispTree(GenTree* tree, case NI_System_Math_Max: printf(" max"); break; + case NI_System_Math_MaxMagnitude: + printf(" maxMagnitude"); + break; + case NI_System_Math_MaxMagnitudeNumber: + printf(" maxMagnitudeNumber"); + break; + case NI_System_Math_MaxNumber: + printf(" maxNumber"); + break; case NI_System_Math_Min: printf(" min"); break; + case NI_System_Math_MinMagnitude: + printf(" minMagnitude"); + break; + case NI_System_Math_MinMagnitudeNumber: + printf(" minMagnitudeNumber"); + break; + case NI_System_Math_MinNumber: + printf(" minNumber"); + break; case NI_System_Math_Pow: printf(" pow"); break; diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 14f1ba67548892..eb67240252e6ae 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -3398,161 +3398,86 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, retNode = impMathIntrinsic(method, sig, callType, ni, tailCall); break; } -#if defined(TARGET_ARM64) - // ARM64 has fmax/fmin which are IEEE754:2019 minimum/maximum compatible - case NI_System_Math_Max: - case NI_System_Math_Min: -#endif -#if defined(FEATURE_HW_INTRINSICS) && defined(TARGET_XARCH) case NI_System_Math_Max: - case NI_System_Math_Min: { - assert(varTypeIsFloating(callType)); - assert(sig->numArgs == 2); - - GenTreeDblCon* cnsNode = nullptr; - GenTree* otherNode = nullptr; - - GenTree* op2 = impStackTop().val; - GenTree* op1 = impStackTop(1).val; - - if (op2->IsCnsFltOrDbl()) - { - cnsNode = op2->AsDblCon(); - otherNode = op1; - } - else if (op1->IsCnsFltOrDbl()) - { - cnsNode = op1->AsDblCon(); - otherNode = op2; - } - - if (cnsNode == nullptr) - { - // no constant node, nothing to do - break; - } - - if (otherNode->IsCnsFltOrDbl()) - { - // both are constant, we can fold this operation completely. Pop both peeked values - - if (ni == NI_System_Math_Max) - { - cnsNode->SetDconValue( - FloatingPointUtils::maximum(cnsNode->DconValue(), otherNode->AsDblCon()->DconValue())); - } - else - { - assert(ni == NI_System_Math_Min); - cnsNode->SetDconValue( - FloatingPointUtils::minimum(cnsNode->DconValue(), otherNode->AsDblCon()->DconValue())); - } - - retNode = cnsNode; - - impPopStack(); - impPopStack(); - DEBUG_DESTROY_NODE(otherNode); - - break; - } + const bool isMax = true; + const bool isMagnitude = false; + const bool isNumber = false; - // only one is constant, we can fold in specialized scenarios - - if (cnsNode->IsFloatNaN()) - { - impSpillSideEffects(false, CHECK_SPILL_ALL DEBUGARG("spill side effects before propagating NaN")); - - // maxsd, maxss, minsd, and minss all return op2 if either is NaN - // we require NaN to be propagated so ensure the known NaN is op2 - - impPopStack(); - impPopStack(); - DEBUG_DESTROY_NODE(otherNode); - - retNode = cnsNode; - break; - } - - if (!compOpportunisticallyDependsOn(InstructionSet_SSE2)) - { - break; - } - - if (ni == NI_System_Math_Max) - { - // maxsd, maxss return op2 if both inputs are 0 of either sign - // we require +0 to be greater than -0, so we can't handle if - // the known constant is +0. This is because if the unknown value - // is -0, we'd need the cns to be op2. But if the unknown value - // is NaN, we'd need the cns to be op1 instead. - - if (cnsNode->IsFloatPositiveZero()) - { - break; - } - - // Given the checks, op1 can safely be the cns and op2 the other node - - ni = (callType == TYP_DOUBLE) ? NI_SSE2_Max : NI_SSE_Max; - - // one is constant and we know its something we can handle, so pop both peeked values + retNode = impMinMaxIntrinsic(method, sig, callJitType, ni, tailCall, isMax, isMagnitude, isNumber); + break; + } - op1 = cnsNode; - op2 = otherNode; - } - else - { - assert(ni == NI_System_Math_Min); + case NI_System_Math_Min: + { + const bool isMax = false; + const bool isMagnitude = false; + const bool isNumber = false; - // minsd, minss return op2 if both inputs are 0 of either sign - // we require -0 to be lesser than +0, so we can't handle if - // the known constant is -0. This is because if the unknown value - // is +0, we'd need the cns to be op2. But if the unknown value - // is NaN, we'd need the cns to be op1 instead. + retNode = impMinMaxIntrinsic(method, sig, callJitType, ni, tailCall, isMax, isMagnitude, isNumber); + break; + } - if (cnsNode->IsFloatNegativeZero()) - { - break; - } + case NI_System_Math_MaxMagnitude: + { + const bool isMax = true; + const bool isMagnitude = true; + const bool isNumber = false; - // Given the checks, op1 can safely be the cns and op2 the other node + retNode = impMinMaxIntrinsic(method, sig, callJitType, ni, tailCall, isMax, isMagnitude, isNumber); + break; + } - ni = (callType == TYP_DOUBLE) ? NI_SSE2_Min : NI_SSE_Min; + case NI_System_Math_MinMagnitude: + { + const bool isMax = false; + const bool isMagnitude = true; + const bool isNumber = false; - // one is constant and we know its something we can handle, so pop both peeked values + retNode = impMinMaxIntrinsic(method, sig, callJitType, ni, tailCall, isMax, isMagnitude, isNumber); + break; + } - op1 = cnsNode; - op2 = otherNode; - } + case NI_System_Math_MaxMagnitudeNumber: + { + const bool isMax = true; + const bool isMagnitude = true; + const bool isNumber = true; - assert(op1->IsCnsFltOrDbl() && !op2->IsCnsFltOrDbl()); + retNode = impMinMaxIntrinsic(method, sig, callJitType, ni, tailCall, isMax, isMagnitude, isNumber); + break; + } - impPopStack(); - impPopStack(); + case NI_System_Math_MinMagnitudeNumber: + { + const bool isMax = false; + const bool isMagnitude = true; + const bool isNumber = true; - GenTreeVecCon* vecCon = gtNewVconNode(TYP_SIMD16); + retNode = impMinMaxIntrinsic(method, sig, callJitType, ni, tailCall, isMax, isMagnitude, isNumber); + break; + } - if (callJitType == CORINFO_TYPE_FLOAT) - { - vecCon->gtSimdVal.f32[0] = (float)op1->AsDblCon()->DconValue(); - } - else - { - vecCon->gtSimdVal.f64[0] = op1->AsDblCon()->DconValue(); - } + case NI_System_Math_MaxNumber: + { + const bool isMax = true; + const bool isMagnitude = false; + const bool isNumber = true; - op1 = vecCon; - op2 = gtNewSimdCreateScalarUnsafeNode(TYP_SIMD16, op2, callJitType, 16); + retNode = impMinMaxIntrinsic(method, sig, callJitType, ni, tailCall, isMax, isMagnitude, isNumber); + break; + } - retNode = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, op2, ni, callJitType, 16); - retNode = gtNewSimdHWIntrinsicNode(callType, retNode, NI_Vector128_ToScalar, callJitType, 16); + case NI_System_Math_MinNumber: + { + const bool isMax = false; + const bool isMagnitude = false; + const bool isNumber = true; + retNode = impMinMaxIntrinsic(method, sig, callJitType, ni, tailCall, isMax, isMagnitude, isNumber); break; } -#endif case NI_System_Math_Pow: case NI_System_Math_Round: @@ -6720,7 +6645,13 @@ bool Compiler::IsMathIntrinsic(NamedIntrinsic intrinsicName) case NI_System_Math_Log2: case NI_System_Math_Log10: case NI_System_Math_Max: + case NI_System_Math_MaxMagnitude: + case NI_System_Math_MaxMagnitudeNumber: + case NI_System_Math_MaxNumber: case NI_System_Math_Min: + case NI_System_Math_MinMagnitude: + case NI_System_Math_MinMagnitudeNumber: + case NI_System_Math_MinNumber: case NI_System_Math_Pow: case NI_System_Math_Round: case NI_System_Math_Sin: @@ -7994,173 +7925,651 @@ GenTree* Compiler::impMathIntrinsic(CORINFO_METHOD_HANDLE method, } //------------------------------------------------------------------------ -// lookupNamedIntrinsic: map method to jit named intrinsic value +// impMinMaxIntrinsic: Imports a min or max intrinsic // // Arguments: -// method -- method handle for method -// -// Return Value: -// Id for the named intrinsic, or Illegal if none. -// -// Notes: -// method should have CORINFO_FLG_INTRINSIC set in its attributes, -// otherwise it is not a named jit intrinsic. +// method - The handle of the method being imported +// callType - The underlying type for the call +// intrinsicName - The intrinsic being imported +// tailCall - true if the method is a tail call; otherwise false +// isMax - true if the intrinsic computes the max; false for the min +// isMagnitude - true if the intrinsic compares using the absolute value of the inputs +// isNumber - true if the intrinsic propagates the number; false for NaN // -NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) +GenTree* Compiler::impMinMaxIntrinsic(CORINFO_METHOD_HANDLE method, + CORINFO_SIG_INFO* sig, + CorInfoType callJitType, + NamedIntrinsic intrinsicName, + bool tailCall, + bool isMax, + bool isMagnitude, + bool isNumber) { - const char* className = nullptr; - const char* namespaceName = nullptr; - const char* enclosingClassName = nullptr; - const char* methodName = - info.compCompHnd->getMethodNameFromMetadata(method, &className, &namespaceName, &enclosingClassName); + var_types callType = JITtype2varType(callJitType); - JITDUMP("Named Intrinsic "); + assert(varTypeIsFloating(callType)); + assert(sig->numArgs == 2); - if (namespaceName != nullptr) - { - JITDUMP("%s.", namespaceName); - } - if (enclosingClassName != nullptr) - { - JITDUMP("%s.", enclosingClassName); - } - if (className != nullptr) + GenTreeDblCon* cnsNode = nullptr; + GenTree* otherNode = nullptr; + + GenTree* op2 = impStackTop().val; + GenTree* op1 = impStackTop(1).val; + + if (op2->IsCnsFltOrDbl()) { - JITDUMP("%s.", className); + cnsNode = op2->AsDblCon(); + otherNode = op1; } - if (methodName != nullptr) + else if (op1->IsCnsFltOrDbl()) { - JITDUMP("%s", methodName); + cnsNode = op1->AsDblCon(); + otherNode = op2; } - if ((namespaceName == nullptr) || (className == nullptr) || (methodName == nullptr)) + if (cnsNode != nullptr) { - // Check if we are dealing with an MD array's known runtime method - CorInfoArrayIntrinsic arrayFuncIndex = info.compCompHnd->getArrayIntrinsicID(method); - switch (arrayFuncIndex) + if (otherNode->IsCnsFltOrDbl()) { - case CorInfoArrayIntrinsic::GET: - JITDUMP("ARRAY_FUNC_GET: Recognized\n"); - return NI_Array_Get; - case CorInfoArrayIntrinsic::SET: - JITDUMP("ARRAY_FUNC_SET: Recognized\n"); - return NI_Array_Set; - case CorInfoArrayIntrinsic::ADDRESS: - JITDUMP("ARRAY_FUNC_ADDRESS: Recognized\n"); - return NI_Array_Address; - default: - break; - } - - JITDUMP(": Not recognized, not enough metadata\n"); - return NI_Illegal; - } - - JITDUMP(": "); - - NamedIntrinsic result = NI_Illegal; + // both are constant, we can fold this operation completely. Pop both peeked values - if (strncmp(namespaceName, "System", 6) == 0) - { - namespaceName += 6; + double x = cnsNode->DconValue(); + double y = otherNode->AsDblCon()->DconValue(); + double z; - if (namespaceName[0] == '\0') - { - switch (className[0]) + if (isMax) { - case 'A': + if (isMagnitude) { - if (strcmp(className, "Activator") == 0) + if (isNumber) { - if (strcmp(methodName, "AllocatorOf") == 0) - { - result = NI_System_Activator_AllocatorOf; - } - else if (strcmp(methodName, "DefaultConstructorOf") == 0) - { - result = NI_System_Activator_DefaultConstructorOf; - } + z = FloatingPointUtils::maximumMagnitudeNumber(x, y); } - else if (strcmp(className, "Array") == 0) + else { - if (strcmp(methodName, "Clone") == 0) - { - result = NI_System_Array_Clone; - } - else if (strcmp(methodName, "GetLength") == 0) - { - result = NI_System_Array_GetLength; - } - else if (strcmp(methodName, "GetLowerBound") == 0) - { - result = NI_System_Array_GetLowerBound; - } - else if (strcmp(methodName, "GetUpperBound") == 0) - { - result = NI_System_Array_GetUpperBound; - } + z = FloatingPointUtils::maximumMagnitude(x, y); } - break; } - - case 'B': + else if (isNumber) { - if (strcmp(className, "BitConverter") == 0) + z = FloatingPointUtils::maximumNumber(x, y); + } + else + { + z = FloatingPointUtils::maximum(x, y); + } + } + else + { + if (isMagnitude) + { + if (isNumber) { - if (strcmp(methodName, "DoubleToInt64Bits") == 0) - { - result = NI_System_BitConverter_DoubleToInt64Bits; - } - else if (strcmp(methodName, "DoubleToUInt64Bits") == 0) - { - result = NI_System_BitConverter_DoubleToInt64Bits; - } - else if (strcmp(methodName, "Int32BitsToSingle") == 0) - { - result = NI_System_BitConverter_Int32BitsToSingle; - } - else if (strcmp(methodName, "Int64BitsToDouble") == 0) - { - result = NI_System_BitConverter_Int64BitsToDouble; - } - else if (strcmp(methodName, "SingleToInt32Bits") == 0) - { - result = NI_System_BitConverter_SingleToInt32Bits; - } - else if (strcmp(methodName, "SingleToUInt32Bits") == 0) - { - result = NI_System_BitConverter_SingleToInt32Bits; - } - else if (strcmp(methodName, "UInt32BitsToSingle") == 0) - { - result = NI_System_BitConverter_Int32BitsToSingle; - } - else if (strcmp(methodName, "UInt64BitsToDouble") == 0) - { - result = NI_System_BitConverter_Int64BitsToDouble; - } + z = FloatingPointUtils::minimumMagnitudeNumber(x, y); } - else if (strcmp(className, "Buffer") == 0) + else { - if (strcmp(methodName, "Memmove") == 0) - { - result = NI_System_Buffer_Memmove; - } + z = FloatingPointUtils::minimumMagnitude(x, y); } - break; } - - case 'E': + else if (isNumber) { - if (strcmp(className, "Enum") == 0) - { - if (strcmp(methodName, "HasFlag") == 0) - { - result = NI_System_Enum_HasFlag; - } - } - else if (strcmp(className, "EETypePtr") == 0) - { + z = FloatingPointUtils::minimumNumber(x, y); + } + else + { + z = FloatingPointUtils::minimum(x, y); + } + } + cnsNode->SetDconValue(z); + + impPopStack(); + impPopStack(); + + DEBUG_DESTROY_NODE(otherNode); + return cnsNode; + } + + // only one is constant, we can fold in specialized scenarios + + if (cnsNode->IsFloatNaN()) + { + impSpillSideEffects(false, CHECK_SPILL_ALL DEBUGARG("spill side effects before propagating NaN")); + + impPopStack(); + impPopStack(); + + if (isNumber) + { + DEBUG_DESTROY_NODE(cnsNode); + return otherNode; + } + else + { + DEBUG_DESTROY_NODE(otherNode); + return cnsNode; + } + } + +#if defined(FEATURE_HW_INTRINSICS) && defined(TARGET_XARCH) + if (!isMagnitude && compOpportunisticallyDependsOn(InstructionSet_SSE2)) + { + bool needsFixup = false; + bool canHandle = false; + + if (isMax) + { + // maxsd, maxss return op2 if both inputs are 0 of either sign + // we require +0 to be greater than -0 we also require NaN to + // not be propagated for isNumber and to be propagated otherwise. + // + // This means for isNumber we want to do `max other, cns` and + // can only handle cns being -0 if Avx512F is supported. This is + // because if other was NaN, we want to return the non-NaN cns. + // But if cns was -0 and other was +0 we'd want to return +0 and + // so need to be able to fixup the result. + // + // For !isNumber we have the inverse and want `max cns, other` and + // can only handle cns being +0 if Avx512F is supported. This is + // because if other was NaN, we want to return other and if cns + // was +0 and other was -0 we'd want to return +0 and so need + // so need to be able to fixup the result. + + if (isNumber) + { + needsFixup = cnsNode->IsFloatNegativeZero(); + } + else + { + needsFixup = cnsNode->IsFloatPositiveZero(); + } + + if (!needsFixup || compOpportunisticallyDependsOn(InstructionSet_AVX512F)) + { + // Given the checks, op1 can safely be the cns and op2 the other node + + intrinsicName = (callType == TYP_DOUBLE) ? NI_SSE2_Max : NI_SSE_Max; + + // one is constant and we know its something we can handle, so pop both peeked values + + op1 = cnsNode; + op2 = otherNode; + + canHandle = true; + } + } + else + { + // minsd, minss return op2 if both inputs are 0 of either sign + // we require -0 to be lesser than +0, we also require NaN to + // not be propagated for isNumber and to be propagated otherwise. + // + // This means for isNumber we want to do `min other, cns` and + // can only handle cns being +0 if Avx512F is supported. This is + // because if other was NaN, we want to return the non-NaN cns. + // But if cns was +0 and other was -0 we'd want to return -0 and + // so need to be able to fixup the result. + // + // For !isNumber we have the inverse and want `min cns, other` and + // can only handle cns being -0 if Avx512F is supported. This is + // because if other was NaN, we want to return other and if cns + // was -0 and other was +0 we'd want to return -0 and so need + // so need to be able to fixup the result. + + if (isNumber) + { + needsFixup = cnsNode->IsFloatNegativeZero(); + } + else + { + needsFixup = cnsNode->IsFloatPositiveZero(); + } + + if (!needsFixup || compOpportunisticallyDependsOn(InstructionSet_AVX512F)) + { + // Given the checks, op1 can safely be the cns and op2 the other node + + intrinsicName = (callType == TYP_DOUBLE) ? NI_SSE2_Min : NI_SSE_Min; + + // one is constant and we know its something we can handle, so pop both peeked values + + op1 = cnsNode; + op2 = otherNode; + + canHandle = true; + } + } + + if (canHandle) + { + assert(op1->IsCnsFltOrDbl() && !op2->IsCnsFltOrDbl()); + + impPopStack(); + impPopStack(); + + GenTreeVecCon* vecCon = gtNewVconNode(TYP_SIMD16); + + if (callJitType == CORINFO_TYPE_FLOAT) + { + vecCon->gtSimdVal.f32[0] = static_cast(op1->AsDblCon()->DconValue()); + } + else + { + vecCon->gtSimdVal.f64[0] = op1->AsDblCon()->DconValue(); + } + + op1 = vecCon; + op2 = gtNewSimdCreateScalarUnsafeNode(TYP_SIMD16, op2, callJitType, 16); + + GenTree* retNode = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, op2, intrinsicName, callJitType, 16); + + if (needsFixup) + { + GenTree* op2Clone; + op2 = impCloneExpr(op2, &op2Clone, CHECK_SPILL_ALL, + nullptr DEBUGARG("Cloning non-constant for Math.Max/Min")); + + retNode->AsHWIntrinsic()->Op(2) = op2; + + GenTreeVecCon* tbl = gtNewVconNode(TYP_SIMD16); + + // FixupScalar(left, right, table, control) computes the input type of right + // adjusts it based on the table and then returns + // + // In our case, left is going to be the result of the RangeScalar operation + // and right is going to be op1 or op2. In the case op1/op2 is QNaN or SNaN + // we want to preserve it instead. Otherwise we want to preserve the original + // result computed by RangeScalar. + // + // If both inputs are NaN, then we'll end up taking op1 by virtue of it being + // the latter fixup. + + if (isMax) + { + // QNAN: 0b0000: Preserve left + // SNAN: 0b0000 + // ZERO: 0b1000: +0 + // +ONE: 0b0000 + // -INF: 0b0000 + // +INF: 0b0000 + // -VAL: 0b0000 + // +VAL: 0b0000 + tbl->gtSimdVal.i32[0] = 0x0800; + } + else + { + // QNAN: 0b0000: Preserve left + // SNAN: 0b0000 + // ZERO: 0b0111: -0 + // +ONE: 0b0000 + // -INF: 0b0000 + // +INF: 0b0000 + // -VAL: 0b0000 + // +VAL: 0b0000 + tbl->gtSimdVal.i32[0] = 0x0700; + } + + retNode = gtNewSimdHWIntrinsicNode(TYP_SIMD16, retNode, op2Clone, tbl, gtNewIconNode(0), + NI_AVX512F_FixupScalar, callJitType, 16); + } + + if (isNumber) + { + std::swap(op1, op2); + + retNode->AsHWIntrinsic()->Op(1) = op2; + retNode->AsHWIntrinsic()->Op(2) = op1; + } + + return gtNewSimdHWIntrinsicNode(callType, retNode, NI_Vector128_ToScalar, callJitType, 16); + } + } +#endif // FEATURE_HW_INTRINSICS && TARGET_XARCH + } + +#if defined(FEATURE_HW_INTRINSICS) && defined(TARGET_XARCH) + if (compOpportunisticallyDependsOn(InstructionSet_AVX512DQ)) + { + // We are constructing a chain of intrinsics similar to: + // var op1 = Vector128.CreateScalarUnsafe(x); + // var op2 = Vector128.CreateScalarUnsafe(y); + // + // var tmp = Avx512DQ.RangeScalar(op1, op2, imm8); + // var tbl = Vector128.CreateScalarUnsafe(0x00); + // + // tmp = Avx512F.FixupScalar(tmp, op2, tbl, 0x00); + // tmp = Avx512F.FixupScalar(tmp, op1, tbl, 0x00); + // + // return tmp.ToScalar(); + + // RangeScalar operates by default almost as MaxNumber or MinNumber + // but, it propagates sNaN and does not propagate qNaN. So we need + // an additional fixup to ensure we propagate qNaN as well. + + uint8_t imm8; + + if (isMax) + { + if (isMagnitude) + { + // 0b01_11: Sign(CompareResult), Max-Abs Value + imm8 = 0x07; + } + else + { + // 0b01_01: Sign(CompareResult), Max Value + imm8 = 0x05; + } + } + else if (isMagnitude) + { + // 0b01_10: Sign(CompareResult), Min-Abs Value + imm8 = 0x06; + } + else + { + // 0b01_00: Sign(CompareResult), Min Value + imm8 = 0x04; + } + + GenTree* op3 = gtNewIconNode(imm8); + GenTree* op2 = gtNewSimdCreateScalarUnsafeNode(TYP_SIMD16, impPopStack().val, callJitType, 16); + GenTree* op1 = gtNewSimdCreateScalarUnsafeNode(TYP_SIMD16, impPopStack().val, callJitType, 16); + + GenTree* op2Clone; + op2 = impCloneExpr(op2, &op2Clone, CHECK_SPILL_ALL, nullptr DEBUGARG("Cloning op2 for Math.Max/Min")); + + GenTree* op1Clone; + op1 = impCloneExpr(op1, &op1Clone, CHECK_SPILL_ALL, nullptr DEBUGARG("Cloning op1 for Math.Max/Min")); + + GenTree* tmp = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1, op2, op3, NI_AVX512DQ_RangeScalar, callJitType, 16); + + // FixupScalar(left, right, table, control) computes the input type of right + // adjusts it based on the table and then returns + // + // In our case, left is going to be the result of the RangeScalar operation, + // which is either sNaN or a normal value, and right is going to be op1 or op2. + + GenTree* tbl1 = gtNewVconNode(TYP_SIMD16); + GenTree* tbl2; + + // We currently have (commutative) + // * snan, snan = snan + // * snan, qnan = snan + // * snan, norm = snan + // * qnan, qnan = qnan + // * qnan, norm = norm + // * norm, norm = norm + + if (isNumber) + { + // We need to fixup the case of: + // * snan, norm = snan + // + // Instead, it should be: + // * snan, norm = norm + + // First look at op1 and op2 using op2 as the classification + // + // If op2 is norm, we take op2 (norm) + // If op2 is nan, we take op1 ( nan or norm) + // + // Thus, if one input was norm the fixup is now norm + + // QNAN: 0b0000: Preserve left + // SNAN: 0b0000 + // ZERO: 0b0001: Preserve right + // +ONE: 0b0001 + // -INF: 0b0001 + // +INF: 0b0001 + // -VAL: 0b0001 + // +VAL: 0b0001 + tbl1->AsVecCon()->gtSimdVal.i32[0] = 0x11111100; + + // Next look at result and fixup using result as the classification + // + // If result is norm, we take the result (norm) + // If result is nan, we take the fixup ( nan or norm) + // + // Thus if either input was snan, we now have norm as expected + // Otherwise, the result was already correct + + tbl1 = impCloneExpr(tbl1, &tbl2, CHECK_SPILL_ALL, nullptr DEBUGARG("Cloning tbl for Math.Max/Min")); + + op1Clone = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1Clone, op2Clone, tbl1, gtNewIconNode(0), + NI_AVX512F_FixupScalar, callJitType, 16); + + tmp = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1Clone, tmp, tbl2, gtNewIconNode(0), NI_AVX512F_FixupScalar, + callJitType, 16); + } + else + { + // We need to fixup the case of: + // * qnan, norm = norm + // + // Instead, it should be: + // * qnan, norm = qnan + + // First look at op1 and op2 using op2 as the classification + // + // If op2 is norm, we take op1 ( nan or norm) + // If op2 is snan, we take op1 ( nan or norm) + // If op2 is qnan, we take op2 (qnan) + // + // Thus, if either input was qnan the fixup is now qnan + + // QNAN: 0b0001: Preserve right + // SNAN: 0b0000: Preserve left + // ZERO: 0b0000 + // +ONE: 0b0000 + // -INF: 0b0000 + // +INF: 0b0000 + // -VAL: 0b0000 + // +VAL: 0b0000 + tbl1->AsVecCon()->gtSimdVal.i32[0] = 0x00000001; + + // Next look at result and fixup using fixup as the classification + // + // If fixup is norm, we take the result (norm) + // If fixup is sNaN, we take the result (sNaN) + // If fixup is qNaN, we take the fixup (qNaN) + // + // Thus if the fixup was qnan, we now have qnan as expected + // Otherwise, the result was already correct + + tbl1 = impCloneExpr(tbl1, &tbl2, CHECK_SPILL_ALL, nullptr DEBUGARG("Cloning tbl for Math.Max/Min")); + + op1Clone = gtNewSimdHWIntrinsicNode(TYP_SIMD16, op1Clone, op2Clone, tbl1, gtNewIconNode(0), + NI_AVX512F_FixupScalar, callJitType, 16); + + tmp = gtNewSimdHWIntrinsicNode(TYP_SIMD16, tmp, op1Clone, tbl2, gtNewIconNode(0), NI_AVX512F_FixupScalar, + callJitType, 16); + } + + return gtNewSimdHWIntrinsicNode(callType, tmp, NI_Vector128_ToScalar, callJitType, 16); + } +#endif // FEATURE_HW_INTRINSICS && TARGET_XARCH + + return impMathIntrinsic(method, sig, callType, intrinsicName, tailCall); +} + +//------------------------------------------------------------------------ +// lookupNamedIntrinsic: map method to jit named intrinsic value +// +// Arguments: +// method -- method handle for method +// +// Return Value: +// Id for the named intrinsic, or Illegal if none. +// +// Notes: +// method should have CORINFO_FLG_INTRINSIC set in its attributes, +// otherwise it is not a named jit intrinsic. +// +NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) +{ + const char* className = nullptr; + const char* namespaceName = nullptr; + const char* enclosingClassName = nullptr; + const char* methodName = + info.compCompHnd->getMethodNameFromMetadata(method, &className, &namespaceName, &enclosingClassName); + + JITDUMP("Named Intrinsic "); + + if (namespaceName != nullptr) + { + JITDUMP("%s.", namespaceName); + } + if (enclosingClassName != nullptr) + { + JITDUMP("%s.", enclosingClassName); + } + if (className != nullptr) + { + JITDUMP("%s.", className); + } + if (methodName != nullptr) + { + JITDUMP("%s", methodName); + } + + if ((namespaceName == nullptr) || (className == nullptr) || (methodName == nullptr)) + { + // Check if we are dealing with an MD array's known runtime method + CorInfoArrayIntrinsic arrayFuncIndex = info.compCompHnd->getArrayIntrinsicID(method); + switch (arrayFuncIndex) + { + case CorInfoArrayIntrinsic::GET: + JITDUMP("ARRAY_FUNC_GET: Recognized\n"); + return NI_Array_Get; + case CorInfoArrayIntrinsic::SET: + JITDUMP("ARRAY_FUNC_SET: Recognized\n"); + return NI_Array_Set; + case CorInfoArrayIntrinsic::ADDRESS: + JITDUMP("ARRAY_FUNC_ADDRESS: Recognized\n"); + return NI_Array_Address; + default: + break; + } + + JITDUMP(": Not recognized, not enough metadata\n"); + return NI_Illegal; + } + + JITDUMP(": "); + + NamedIntrinsic result = NI_Illegal; + + if (strncmp(namespaceName, "System", 6) == 0) + { + namespaceName += 6; + + if (namespaceName[0] == '\0') + { + switch (className[0]) + { + case 'A': + { + if (strcmp(className, "Activator") == 0) + { + if (strcmp(methodName, "AllocatorOf") == 0) + { + result = NI_System_Activator_AllocatorOf; + } + else if (strcmp(methodName, "DefaultConstructorOf") == 0) + { + result = NI_System_Activator_DefaultConstructorOf; + } + } + else if (strcmp(className, "Array") == 0) + { + if (strcmp(methodName, "Clone") == 0) + { + result = NI_System_Array_Clone; + } + else if (strcmp(methodName, "GetLength") == 0) + { + result = NI_System_Array_GetLength; + } + else if (strcmp(methodName, "GetLowerBound") == 0) + { + result = NI_System_Array_GetLowerBound; + } + else if (strcmp(methodName, "GetUpperBound") == 0) + { + result = NI_System_Array_GetUpperBound; + } + } + break; + } + + case 'B': + { + if (strcmp(className, "BitConverter") == 0) + { + if (strcmp(methodName, "DoubleToInt64Bits") == 0) + { + result = NI_System_BitConverter_DoubleToInt64Bits; + } + else if (strcmp(methodName, "DoubleToUInt64Bits") == 0) + { + result = NI_System_BitConverter_DoubleToInt64Bits; + } + else if (strcmp(methodName, "Int32BitsToSingle") == 0) + { + result = NI_System_BitConverter_Int32BitsToSingle; + } + else if (strcmp(methodName, "Int64BitsToDouble") == 0) + { + result = NI_System_BitConverter_Int64BitsToDouble; + } + else if (strcmp(methodName, "SingleToInt32Bits") == 0) + { + result = NI_System_BitConverter_SingleToInt32Bits; + } + else if (strcmp(methodName, "SingleToUInt32Bits") == 0) + { + result = NI_System_BitConverter_SingleToInt32Bits; + } + else if (strcmp(methodName, "UInt32BitsToSingle") == 0) + { + result = NI_System_BitConverter_Int32BitsToSingle; + } + else if (strcmp(methodName, "UInt64BitsToDouble") == 0) + { + result = NI_System_BitConverter_Int64BitsToDouble; + } + } + else if (strcmp(className, "Buffer") == 0) + { + if (strcmp(methodName, "Memmove") == 0) + { + result = NI_System_Buffer_Memmove; + } + } + break; + } + + case 'D': + { + if (strcmp(className, "Double") == 0) + { + result = lookupPrimitiveFloatNamedIntrinsic(method, methodName); + } + break; + } + + case 'E': + { + if (strcmp(className, "Enum") == 0) + { + if (strcmp(methodName, "HasFlag") == 0) + { + result = NI_System_Enum_HasFlag; + } + } + else if (strcmp(className, "EETypePtr") == 0) + { if (strcmp(methodName, "EETypePtrOf") == 0) { result = NI_System_EETypePtr_EETypePtrOf; @@ -8186,7 +8595,7 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) if ((strcmp(className, "Int32") == 0) || (strcmp(className, "Int64") == 0) || (strcmp(className, "IntPtr") == 0)) { - result = lookupPrimitiveNamedIntrinsic(method, methodName); + result = lookupPrimitiveIntNamedIntrinsic(method, methodName); } break; } @@ -8195,126 +8604,7 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) { if ((strcmp(className, "Math") == 0) || (strcmp(className, "MathF") == 0)) { - if (strcmp(methodName, "Abs") == 0) - { - result = NI_System_Math_Abs; - } - else if (strcmp(methodName, "Acos") == 0) - { - result = NI_System_Math_Acos; - } - else if (strcmp(methodName, "Acosh") == 0) - { - result = NI_System_Math_Acosh; - } - else if (strcmp(methodName, "Asin") == 0) - { - result = NI_System_Math_Asin; - } - else if (strcmp(methodName, "Asinh") == 0) - { - result = NI_System_Math_Asinh; - } - else if (strcmp(methodName, "Atan") == 0) - { - result = NI_System_Math_Atan; - } - else if (strcmp(methodName, "Atanh") == 0) - { - result = NI_System_Math_Atanh; - } - else if (strcmp(methodName, "Atan2") == 0) - { - result = NI_System_Math_Atan2; - } - else if (strcmp(methodName, "Cbrt") == 0) - { - result = NI_System_Math_Cbrt; - } - else if (strcmp(methodName, "Ceiling") == 0) - { - result = NI_System_Math_Ceiling; - } - else if (strcmp(methodName, "Cos") == 0) - { - result = NI_System_Math_Cos; - } - else if (strcmp(methodName, "Cosh") == 0) - { - result = NI_System_Math_Cosh; - } - else if (strcmp(methodName, "Exp") == 0) - { - result = NI_System_Math_Exp; - } - else if (strcmp(methodName, "Floor") == 0) - { - result = NI_System_Math_Floor; - } - else if (strcmp(methodName, "FMod") == 0) - { - result = NI_System_Math_FMod; - } - else if (strcmp(methodName, "FusedMultiplyAdd") == 0) - { - result = NI_System_Math_FusedMultiplyAdd; - } - else if (strcmp(methodName, "ILogB") == 0) - { - result = NI_System_Math_ILogB; - } - else if (strcmp(methodName, "Log") == 0) - { - result = NI_System_Math_Log; - } - else if (strcmp(methodName, "Log2") == 0) - { - result = NI_System_Math_Log2; - } - else if (strcmp(methodName, "Log10") == 0) - { - result = NI_System_Math_Log10; - } - else if (strcmp(methodName, "Max") == 0) - { - result = NI_System_Math_Max; - } - else if (strcmp(methodName, "Min") == 0) - { - result = NI_System_Math_Min; - } - else if (strcmp(methodName, "Pow") == 0) - { - result = NI_System_Math_Pow; - } - else if (strcmp(methodName, "Round") == 0) - { - result = NI_System_Math_Round; - } - else if (strcmp(methodName, "Sin") == 0) - { - result = NI_System_Math_Sin; - } - else if (strcmp(methodName, "Sinh") == 0) - { - result = NI_System_Math_Sinh; - } - else if (strcmp(methodName, "Sqrt") == 0) - { - result = NI_System_Math_Sqrt; - } - else if (strcmp(methodName, "Tan") == 0) - { - result = NI_System_Math_Tan; - } - else if (strcmp(methodName, "Tanh") == 0) - { - result = NI_System_Math_Tanh; - } - else if (strcmp(methodName, "Truncate") == 0) - { - result = NI_System_Math_Truncate; - } + result = lookupPrimitiveFloatNamedIntrinsic(method, methodName); } else if (strcmp(className, "MemoryExtensions") == 0) { @@ -8390,7 +8680,11 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) case 'S': { - if (strcmp(className, "Span`1") == 0) + if (strcmp(className, "Single") == 0) + { + result = lookupPrimitiveFloatNamedIntrinsic(method, methodName); + } + else if (strcmp(className, "Span`1") == 0) { if (strcmp(methodName, "get_Item") == 0) { @@ -8487,7 +8781,7 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) if ((strcmp(className, "UInt32") == 0) || (strcmp(className, "UInt64") == 0) || (strcmp(className, "UIntPtr") == 0)) { - result = lookupPrimitiveNamedIntrinsic(method, methodName); + result = lookupPrimitiveIntNamedIntrinsic(method, methodName); } break; } @@ -8534,7 +8828,7 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) { if (strcmp(className, "BitOperations") == 0) { - result = lookupPrimitiveNamedIntrinsic(method, methodName); + result = lookupPrimitiveIntNamedIntrinsic(method, methodName); } else { @@ -8895,7 +9189,311 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) } //------------------------------------------------------------------------ -// lookupPrimitiveNamedIntrinsic: map method to jit named intrinsic value +// lookupPrimitiveFloatNamedIntrinsic: map method to jit named intrinsic value +// +// Arguments: +// method -- method handle for method +// +// Return Value: +// Id for the named intrinsic, or Illegal if none. +// +// Notes: +// method should have CORINFO_FLG_INTRINSIC set in its attributes, +// otherwise it is not a named jit intrinsic. +// +NamedIntrinsic Compiler::lookupPrimitiveFloatNamedIntrinsic(CORINFO_METHOD_HANDLE method, const char* methodName) +{ + NamedIntrinsic result = NI_Illegal; + + switch (methodName[0]) + { + case 'A': + { + if (strcmp(methodName, "Abs") == 0) + { + result = NI_System_Math_Abs; + } + else if (strncmp(methodName, "Acos", 4) == 0) + { + methodName += 4; + + if (methodName[0] == '\0') + { + result = NI_System_Math_Acos; + } + else if (methodName[1] == '\0') + { + if (methodName[0] == 'h') + { + result = NI_System_Math_Acosh; + } + } + } + else if (strncmp(methodName, "Asin", 4) == 0) + { + methodName += 4; + + if (methodName[0] == '\0') + { + result = NI_System_Math_Asin; + } + else if (methodName[1] == '\0') + { + if (methodName[0] == 'h') + { + result = NI_System_Math_Asinh; + } + } + } + else if (strncmp(methodName, "Atan", 4) == 0) + { + methodName += 4; + + if (methodName[0] == '\0') + { + result = NI_System_Math_Atan; + } + else if (methodName[1] == '\0') + { + if (methodName[0] == 'h') + { + result = NI_System_Math_Atanh; + } + else if (methodName[0] == '2') + { + result = NI_System_Math_Atan2; + } + } + } + break; + } + + case 'C': + { + if (strcmp(methodName, "Cbrt") == 0) + { + result = NI_System_Math_Cbrt; + } + else if (strcmp(methodName, "Ceiling") == 0) + { + result = NI_System_Math_Ceiling; + } + else if (strncmp(methodName, "Cos", 3) == 0) + { + methodName += 3; + + if (methodName[0] == '\0') + { + result = NI_System_Math_Cos; + } + else if (methodName[1] == '\0') + { + if (methodName[0] == 'h') + { + result = NI_System_Math_Cosh; + } + } + } + break; + } + + case 'E': + { + if (strcmp(methodName, "Exp") == 0) + { + result = NI_System_Math_Exp; + } + break; + } + + case 'F': + { + if (strcmp(methodName, "Floor") == 0) + { + result = NI_System_Math_Floor; + } + else if (strcmp(methodName, "FMod") == 0) + { + result = NI_System_Math_FMod; + } + else if (strcmp(methodName, "FusedMultiplyAdd") == 0) + { + result = NI_System_Math_FusedMultiplyAdd; + } + break; + } + + case 'I': + { + if (strcmp(methodName, "ILogB") == 0) + { + result = NI_System_Math_ILogB; + } + break; + } + + case 'L': + { + if (strncmp(methodName, "Log", 3) == 0) + { + methodName += 3; + + if (methodName[0] == '\0') + { + result = NI_System_Math_Log; + } + else if (methodName[1] == '\0') + { + if (methodName[0] == '2') + { + result = NI_System_Math_Log2; + } + } + else if (strcmp(methodName, "10") == 0) + { + result = NI_System_Math_Log10; + } + } + break; + } + + case 'M': + { + if (strncmp(methodName, "Max", 3) == 0) + { + methodName += 3; + + if (methodName[0] == '\0') + { + result = NI_System_Math_Max; + } + else if (strncmp(methodName, "Magnitude", 9) == 0) + { + methodName += 9; + + if (methodName[0] == '\0') + { + result = NI_System_Math_MaxMagnitude; + } + else if (strcmp(methodName, "Number") == 0) + { + result = NI_System_Math_MaxMagnitudeNumber; + } + } + else if (strcmp(methodName, "Number") == 0) + { + result = NI_System_Math_MaxNumber; + } + } + else if (strncmp(methodName, "Min", 3) == 0) + { + methodName += 3; + + if (methodName[0] == '\0') + { + result = NI_System_Math_Min; + } + else if (strncmp(methodName, "Magnitude", 9) == 0) + { + methodName += 9; + + if (methodName[0] == '\0') + { + result = NI_System_Math_MinMagnitude; + } + else if (strcmp(methodName, "Number") == 0) + { + result = NI_System_Math_MinMagnitudeNumber; + } + } + else if (strcmp(methodName, "Number") == 0) + { + result = NI_System_Math_MinNumber; + } + } + break; + } + + case 'P': + { + if (strcmp(methodName, "Pow") == 0) + { + result = NI_System_Math_Pow; + } + break; + } + + case 'R': + { + if (strcmp(methodName, "Round") == 0) + { + result = NI_System_Math_Round; + } + break; + } + + case 'S': + { + if (strncmp(methodName, "Sin", 3) == 0) + { + methodName += 3; + + if (methodName[0] == '\0') + { + result = NI_System_Math_Sin; + } + else if (methodName[1] == '\0') + { + if (methodName[0] == 'h') + { + result = NI_System_Math_Sinh; + } + } + } + else if (strcmp(methodName, "Sqrt") == 0) + { + result = NI_System_Math_Sqrt; + } + break; + } + + case 'T': + { + if (strncmp(methodName, "Tan", 3) == 0) + { + methodName += 3; + + if (methodName[0] == '\0') + { + result = NI_System_Math_Tan; + } + else if (methodName[1] == '\0') + { + if (methodName[0] == 'h') + { + result = NI_System_Math_Tanh; + } + } + result = NI_System_Math_Tan; + } + else if (strcmp(methodName, "Truncate") == 0) + { + result = NI_System_Math_Truncate; + } + break; + } + + default: + { + break; + } + } + + return result; +} + +//------------------------------------------------------------------------ +// lookupPrimitiveIntNamedIntrinsic: map method to jit named intrinsic value // // Arguments: // method -- method handle for method @@ -8907,7 +9505,7 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) // method should have CORINFO_FLG_INTRINSIC set in its attributes, // otherwise it is not a named jit intrinsic. // -NamedIntrinsic Compiler::lookupPrimitiveNamedIntrinsic(CORINFO_METHOD_HANDLE method, const char* methodName) +NamedIntrinsic Compiler::lookupPrimitiveIntNamedIntrinsic(CORINFO_METHOD_HANDLE method, const char* methodName) { NamedIntrinsic result = NI_Illegal; diff --git a/src/coreclr/jit/lsraarm64.cpp b/src/coreclr/jit/lsraarm64.cpp index 23be48377907c3..1d22c1972c0aa6 100644 --- a/src/coreclr/jit/lsraarm64.cpp +++ b/src/coreclr/jit/lsraarm64.cpp @@ -873,6 +873,9 @@ int LinearScan::BuildNode(GenTree* tree) { case NI_System_Math_Max: case NI_System_Math_Min: + case NI_System_Math_MaxNumber: + case NI_System_Math_MinNumber: + { assert(varTypeIsFloating(tree->gtGetOp1())); assert(varTypeIsFloating(tree->gtGetOp2())); assert(tree->gtGetOp1()->TypeIs(tree->TypeGet())); @@ -881,6 +884,7 @@ int LinearScan::BuildNode(GenTree* tree) assert(dstCount == 1); BuildDef(tree); break; + } case NI_System_Math_Abs: case NI_System_Math_Ceiling: @@ -888,6 +892,7 @@ int LinearScan::BuildNode(GenTree* tree) case NI_System_Math_Truncate: case NI_System_Math_Round: case NI_System_Math_Sqrt: + { assert(varTypeIsFloating(tree->gtGetOp1())); assert(tree->gtGetOp1()->TypeIs(tree->TypeGet())); @@ -896,6 +901,7 @@ int LinearScan::BuildNode(GenTree* tree) assert(dstCount == 1); BuildDef(tree); break; + } default: unreached(); diff --git a/src/coreclr/jit/namedintrinsiclist.h b/src/coreclr/jit/namedintrinsiclist.h index 6bc31d315b5c36..1fcfc0930d762e 100644 --- a/src/coreclr/jit/namedintrinsiclist.h +++ b/src/coreclr/jit/namedintrinsiclist.h @@ -44,7 +44,13 @@ enum NamedIntrinsic : unsigned short NI_System_Math_Log2, NI_System_Math_Log10, NI_System_Math_Max, + NI_System_Math_MaxMagnitude, + NI_System_Math_MaxMagnitudeNumber, + NI_System_Math_MaxNumber, NI_System_Math_Min, + NI_System_Math_MinMagnitude, + NI_System_Math_MinMagnitudeNumber, + NI_System_Math_MinNumber, NI_System_Math_Pow, NI_System_Math_Round, NI_System_Math_Sin, diff --git a/src/coreclr/jit/utils.cpp b/src/coreclr/jit/utils.cpp index 76fa1ddb1012a3..93a8292e07ab94 100644 --- a/src/coreclr/jit/utils.cpp +++ b/src/coreclr/jit/utils.cpp @@ -2423,9 +2423,10 @@ bool FloatingPointUtils::isPositiveZero(double val) //------------------------------------------------------------------------ // maximum: This matches the IEEE 754:2019 `maximum` function -// It propagates NaN inputs back to the caller and -// otherwise returns the larger of the inputs. It -// treats +0 as larger than -0 as per the specification. +// +// It propagates NaN inputs back to the caller and +// otherwise returns the greater of the inputs. It +// treats +0 as greater than -0 as per the specification. // // Arguments: // val1 - left operand @@ -2434,7 +2435,6 @@ bool FloatingPointUtils::isPositiveZero(double val) // Return Value: // Either val1 or val2 // - double FloatingPointUtils::maximum(double val1, double val2) { if (val1 != val2) @@ -2443,16 +2443,112 @@ double FloatingPointUtils::maximum(double val1, double val2) { return val2 < val1 ? val1 : val2; } + return val1; } + return isNegative(val2) ? val1 : val2; } +//------------------------------------------------------------------------ +// maximumMagnitude: This matches the IEEE 754:2019 `maximumMagnitude` function +// +// It propagates NaN inputs back to the caller and +// otherwise returns the input with a greater magnitude. +// It treats +0 as greater than -0 as per the specification. +// +// Arguments: +// x - left operand +// y - right operand +// +// Return Value: +// Either x or y +// +double FloatingPointUtils::maximumMagnitude(double x, double y) +{ + double ax = fabs(x); + double ay = fabs(y); + + if ((ax > ay) || isNaN(ax)) + { + return x; + } + + if (ax == ay) + { + return isNegative(x) ? y : x; + } + + return y; +} + +//------------------------------------------------------------------------ +// maximumMagnitudeNumber: // This matches the IEEE 754:2019 `maximumMagnitudeNumber` function +// +// It does not propagate NaN inputs back to the caller and +// otherwise returns the input with a larger magnitude. +// It treats +0 as larger than -0 as per the specification. +// +// Arguments: +// x - left operand +// y - right operand +// +// Return Value: +// Either x or y +// +double FloatingPointUtils::maximumMagnitudeNumber(double x, double y) +{ + double ax = fabs(x); + double ay = fabs(y); + + if ((ax > ay) || isNaN(ay)) + { + return x; + } + + if (ax == ay) + { + return isNegative(x) ? y : x; + } + + return y; +} + +//------------------------------------------------------------------------ +// maximumNumber: This matches the IEEE 754:2019 `maximumNumber` function +// +// It does not propagate NaN inputs back to the caller and +// otherwise returns the larger of the inputs. It +// treats +0 as larger than -0 as per the specification. +// +// Arguments: +// x - left operand +// y - right operand +// +// Return Value: +// Either x or y +// +double FloatingPointUtils::maximumNumber(double x, double y) +{ + if (x != y) + { + if (!isNaN(y)) + { + return y < x ? x : y; + } + + return x; + } + + return isNegative(y) ? x : y; +} + //------------------------------------------------------------------------ // maximum: This matches the IEEE 754:2019 `maximum` function -// It propagates NaN inputs back to the caller and -// otherwise returns the larger of the inputs. It -// treats +0 as larger than -0 as per the specification. +// +// It propagates NaN inputs back to the caller and +// otherwise returns the greater of the inputs. It +// treats +0 as greater than -0 as per the specification. // // Arguments: // val1 - left operand @@ -2461,7 +2557,6 @@ double FloatingPointUtils::maximum(double val1, double val2) // Return Value: // Either val1 or val2 // - float FloatingPointUtils::maximum(float val1, float val2) { if (val1 != val2) @@ -2470,16 +2565,112 @@ float FloatingPointUtils::maximum(float val1, float val2) { return val2 < val1 ? val1 : val2; } + return val1; } + return isNegative(val2) ? val1 : val2; } +//------------------------------------------------------------------------ +// maximumMagnitude: This matches the IEEE 754:2019 `maximumMagnitude` function +// +// It propagates NaN inputs back to the caller and +// otherwise returns the input with a greater magnitude. +// It treats +0 as greater than -0 as per the specification. +// +// Arguments: +// x - left operand +// y - right operand +// +// Return Value: +// Either x or y +// +float FloatingPointUtils::maximumMagnitude(float x, float y) +{ + float ax = fabsf(x); + float ay = fabsf(y); + + if ((ax > ay) || isNaN(ax)) + { + return x; + } + + if (ax == ay) + { + return isNegative(x) ? y : x; + } + + return y; +} + +//------------------------------------------------------------------------ +// maximumMagnitudeNumber: This matches the IEEE 754:2019 `maximumMagnitudeNumber` function +// +// It does not propagate NaN inputs back to the caller and +// otherwise returns the input with a larger magnitude. +// It treats +0 as larger than -0 as per the specification. +// +// Arguments: +// x - left operand +// y - right operand +// +// Return Value: +// Either x or y +// +float FloatingPointUtils::maximumMagnitudeNumber(float x, float y) +{ + float ax = fabsf(x); + float ay = fabsf(y); + + if ((ax > ay) || isNaN(ay)) + { + return x; + } + + if (ax == ay) + { + return isNegative(x) ? y : x; + } + + return y; +} + +//------------------------------------------------------------------------ +// maximumNumber: This matches the IEEE 754:2019 `maximumNumber` function +// +// It does not propagate NaN inputs back to the caller and +// otherwise returns the larger of the inputs. It +// treats +0 as larger than -0 as per the specification. +// +// Arguments: +// x - left operand +// y - right operand +// +// Return Value: +// Either x or y +// +float FloatingPointUtils::maximumNumber(float x, float y) +{ + if (x != y) + { + if (!isNaN(y)) + { + return y < x ? x : y; + } + + return x; + } + + return isNegative(y) ? x : y; +} + //------------------------------------------------------------------------ // minimum: This matches the IEEE 754:2019 `minimum` function -// It propagates NaN inputs back to the caller and -// otherwise returns the larger of the inputs. It -// treats +0 as larger than -0 as per the specification. +// +// It propagates NaN inputs back to the caller and +// otherwise returns the lesser of the inputs. It +// treats +0 as lesser than -0 as per the specification. // // Arguments: // val1 - left operand @@ -2488,21 +2679,120 @@ float FloatingPointUtils::maximum(float val1, float val2) // Return Value: // Either val1 or val2 // - double FloatingPointUtils::minimum(double val1, double val2) { - if (val1 != val2 && !isNaN(val1)) + if (val1 != val2) { - return val1 < val2 ? val1 : val2; + if (!isNaN(val1)) + { + return val1 < val2 ? val1 : val2; + } + + return val1; } + return isNegative(val1) ? val1 : val2; } +//------------------------------------------------------------------------ +// minimumMagnitude: This matches the IEEE 754:2019 `minimumMagnitude` function +// +// It propagates NaN inputs back to the caller and +// otherwise returns the input with a lesser magnitude. +// It treats +0 as lesser than -0 as per the specification. +// +// Arguments: +// x - left operand +// y - right operand +// +// Return Value: +// Either x or y +// +double FloatingPointUtils::minimumMagnitude(double x, double y) +{ + double ax = fabs(x); + double ay = fabs(y); + + if ((ax < ay) || isNaN(ax)) + { + return x; + } + + if (ax == ay) + { + return isNegative(x) ? x : y; + } + + return y; +} + +//------------------------------------------------------------------------ +// minimumMagnitudeNumber: This matches the IEEE 754:2019 `minimumMagnitudeNumber` function +// +// It does not propagate NaN inputs back to the caller and +// otherwise returns the input with a larger magnitude. +// It treats +0 as larger than -0 as per the specification. +// +// Arguments: +// x - left operand +// y - right operand +// +// Return Value: +// Either x or y +// +double FloatingPointUtils::minimumMagnitudeNumber(double x, double y) +{ + double ax = fabs(x); + double ay = fabs(y); + + if ((ax < ay) || isNaN(ay)) + { + return x; + } + + if (ax == ay) + { + return isNegative(x) ? x : y; + } + + return y; +} + +//------------------------------------------------------------------------ +// minimumNumber: This matches the IEEE 754:2019 `minimumNumber` function +// +// It does not propagate NaN inputs back to the caller and +// otherwise returns the larger of the inputs. It +// treats +0 as larger than -0 as per the specification. +// +// Arguments: +// x - left operand +// y - right operand +// +// Return Value: +// Either x or y +// +double FloatingPointUtils::minimumNumber(double x, double y) +{ + if (x != y) + { + if (!isNaN(y)) + { + return x < y ? x : y; + } + + return x; + } + + return isNegative(x) ? x : y; +} + //------------------------------------------------------------------------ // minimum: This matches the IEEE 754:2019 `minimum` function -// It propagates NaN inputs back to the caller and -// otherwise returns the larger of the inputs. It -// treats +0 as larger than -0 as per the specification. +// +// It propagates NaN inputs back to the caller and +// otherwise returns the lesser of the inputs. It +// treats +0 as lesser than -0 as per the specification. // // Arguments: // val1 - left operand @@ -2513,13 +2803,112 @@ double FloatingPointUtils::minimum(double val1, double val2) // float FloatingPointUtils::minimum(float val1, float val2) { - if (val1 != val2 && !isNaN(val1)) + if (val1 != val2) { - return val1 < val2 ? val1 : val2; + if (!isNaN(val1)) + { + return val1 < val2 ? val1 : val2; + } + + return val1; } + return isNegative(val1) ? val1 : val2; } +//------------------------------------------------------------------------ +// minimumMagnitude: This matches the IEEE 754:2019 `minimumMagnitude` function +// +// It propagates NaN inputs back to the caller and +// otherwise returns the input with a lesser magnitude. +// It treats +0 as lesser than -0 as per the specification. +// +// Arguments: +// x - left operand +// y - right operand +// +// Return Value: +// Either x or y +// +float FloatingPointUtils::minimumMagnitude(float x, float y) +{ + float ax = fabsf(x); + float ay = fabsf(y); + + if ((ax < ay) || isNaN(ax)) + { + return x; + } + + if (ax == ay) + { + return isNegative(x) ? x : y; + } + + return y; +} + +//------------------------------------------------------------------------ +// minimumMagnitudeNumber: This matches the IEEE 754:2019 `minimumMagnitudeNumber` function +// +// It does not propagate NaN inputs back to the caller and +// otherwise returns the input with a larger magnitude. +// It treats +0 as larger than -0 as per the specification. +// +// Arguments: +// x - left operand +// y - right operand +// +// Return Value: +// Either x or y +// +float FloatingPointUtils::minimumMagnitudeNumber(float x, float y) +{ + float ax = fabsf(x); + float ay = fabsf(y); + + if ((ax < ay) || isNaN(ay)) + { + return x; + } + + if (ax == ay) + { + return isNegative(x) ? x : y; + } + + return y; +} + +//------------------------------------------------------------------------ +// minimumNumber: This matches the IEEE 754:2019 `minimumNumber` function +// +// It does not propagate NaN inputs back to the caller and +// otherwise returns the larger of the inputs. It +// treats +0 as larger than -0 as per the specification. +// +// Arguments: +// x - left operand +// y - right operand +// +// Return Value: +// Either x or y +// +float FloatingPointUtils::minimumNumber(float x, float y) +{ + if (x != y) + { + if (!isNaN(y)) + { + return x < y ? x : y; + } + + return x; + } + + return isNegative(x) ? x : y; +} + //------------------------------------------------------------------------ // normalize: Normalize a floating point value. // diff --git a/src/coreclr/jit/utils.h b/src/coreclr/jit/utils.h index 32afd1a2e8e665..5cf3501143273b 100644 --- a/src/coreclr/jit/utils.h +++ b/src/coreclr/jit/utils.h @@ -763,12 +763,36 @@ class FloatingPointUtils static double maximum(double val1, double val2); + static double maximumMagnitude(double val1, double val2); + + static double maximumMagnitudeNumber(double val1, double val2); + + static double maximumNumber(double val1, double val2); + static float maximum(float val1, float val2); + static float maximumMagnitude(float val1, float val2); + + static float maximumMagnitudeNumber(float val1, float val2); + + static float maximumNumber(float val1, float val2); + static double minimum(double val1, double val2); + static double minimumMagnitude(double val1, double val2); + + static double minimumMagnitudeNumber(double val1, double val2); + + static double minimumNumber(double val1, double val2); + static float minimum(float val1, float val2); + static float minimumMagnitude(float val1, float val2); + + static float minimumMagnitudeNumber(float val1, float val2); + + static float minimumNumber(float val1, float val2); + static double normalize(double x); }; diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 9951fc1625dc50..51b97de0c54509 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -8357,6 +8357,30 @@ ValueNum ValueNumStore::EvalMathFuncBinary(var_types typ, NamedIntrinsic gtMathF break; } + case NI_System_Math_MaxMagnitude: + { + assert(typ == TypeOfVN(arg1VN)); + double arg1Val = GetConstantDouble(arg1VN); + res = FloatingPointUtils::maximumMagnitude(arg0Val, arg1Val); + break; + } + + case NI_System_Math_MaxMagnitudeNumber: + { + assert(typ == TypeOfVN(arg1VN)); + double arg1Val = GetConstantDouble(arg1VN); + res = FloatingPointUtils::maximumMagnitudeNumber(arg0Val, arg1Val); + break; + } + + case NI_System_Math_MaxNumber: + { + assert(typ == TypeOfVN(arg1VN)); + double arg1Val = GetConstantDouble(arg1VN); + res = FloatingPointUtils::maximumNumber(arg0Val, arg1Val); + break; + } + case NI_System_Math_Min: { assert(typ == TypeOfVN(arg1VN)); @@ -8365,6 +8389,30 @@ ValueNum ValueNumStore::EvalMathFuncBinary(var_types typ, NamedIntrinsic gtMathF break; } + case NI_System_Math_MinMagnitude: + { + assert(typ == TypeOfVN(arg1VN)); + double arg1Val = GetConstantDouble(arg1VN); + res = FloatingPointUtils::minimumMagnitude(arg0Val, arg1Val); + break; + } + + case NI_System_Math_MinMagnitudeNumber: + { + assert(typ == TypeOfVN(arg1VN)); + double arg1Val = GetConstantDouble(arg1VN); + res = FloatingPointUtils::minimumMagnitudeNumber(arg0Val, arg1Val); + break; + } + + case NI_System_Math_MinNumber: + { + assert(typ == TypeOfVN(arg1VN)); + double arg1Val = GetConstantDouble(arg1VN); + res = FloatingPointUtils::minimumNumber(arg0Val, arg1Val); + break; + } + default: // the above are the only binary math intrinsics at the time of this writing. unreached(); @@ -8448,10 +8496,34 @@ ValueNum ValueNumStore::EvalMathFuncBinary(var_types typ, NamedIntrinsic gtMathF vnf = VNF_Max; break; + case NI_System_Math_MaxMagnitude: + vnf = VNF_MaxMagnitude; + break; + + case NI_System_Math_MaxMagnitudeNumber: + vnf = VNF_MaxMagnitudeNumber; + break; + + case NI_System_Math_MaxNumber: + vnf = VNF_MaxNumber; + break; + case NI_System_Math_Min: vnf = VNF_Min; break; + case NI_System_Math_MinMagnitude: + vnf = VNF_MinMagnitude; + break; + + case NI_System_Math_MinMagnitudeNumber: + vnf = VNF_MinMagnitudeNumber; + break; + + case NI_System_Math_MinNumber: + vnf = VNF_MinNumber; + break; + case NI_System_Math_Pow: vnf = VNF_Pow; break; diff --git a/src/coreclr/jit/valuenumfuncs.h b/src/coreclr/jit/valuenumfuncs.h index de0c347591a8d2..f875f3aadaf43d 100644 --- a/src/coreclr/jit/valuenumfuncs.h +++ b/src/coreclr/jit/valuenumfuncs.h @@ -92,7 +92,13 @@ ValueNumFuncDef(Log, 1, false, false, false, false) ValueNumFuncDef(Log2, 1, false, false, false, false) ValueNumFuncDef(Log10, 1, false, false, false, false) ValueNumFuncDef(Max, 2, false, false, false, false) +ValueNumFuncDef(MaxMagnitude, 2, false, false, false, false) +ValueNumFuncDef(MaxMagnitudeNumber, 2, false, false, false, false) +ValueNumFuncDef(MaxNumber, 2, false, false, false, false) ValueNumFuncDef(Min, 2, false, false, false, false) +ValueNumFuncDef(MinMagnitude, 2, false, false, false, false) +ValueNumFuncDef(MinMagnitudeNumber, 2, false, false, false, false) +ValueNumFuncDef(MinNumber, 2, false, false, false, false) ValueNumFuncDef(Pow, 2, false, false, false, false) ValueNumFuncDef(RoundDouble, 1, false, false, false, false) ValueNumFuncDef(RoundInt32, 1, false, false, false, false) diff --git a/src/libraries/System.Private.CoreLib/src/System/Double.cs b/src/libraries/System.Private.CoreLib/src/System/Double.cs index aaa637ae02a6f3..9058eaecf4f238 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Double.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Double.cs @@ -571,6 +571,7 @@ public static bool IsPow2(double value) } /// + [Intrinsic] public static double Log2(double value) => Math.Log2(value); // @@ -624,6 +625,7 @@ public static bool IsPow2(double value) // /// + [Intrinsic] public static double Exp(double x) => Math.Exp(x); /// @@ -646,12 +648,15 @@ public static bool IsPow2(double value) // /// + [Intrinsic] public static double Ceiling(double x) => Math.Ceiling(x); /// + [Intrinsic] public static double Floor(double x) => Math.Floor(x); /// + [Intrinsic] public static double Round(double x) => Math.Round(x); /// @@ -664,6 +669,7 @@ public static bool IsPow2(double value) public static double Round(double x, int digits, MidpointRounding mode) => Math.Round(x, digits, mode); /// + [Intrinsic] public static double Truncate(double x) => Math.Truncate(x); /// @@ -819,6 +825,7 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destinati static double IFloatingPointIeee754.PositiveInfinity => PositiveInfinity; /// + [Intrinsic] public static double Atan2(double y, double x) => Math.Atan2(y, x); /// @@ -831,6 +838,7 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destinati public static double BitIncrement(double x) => Math.BitIncrement(x); /// + [Intrinsic] public static double FusedMultiplyAdd(double left, double right, double addend) => Math.FusedMultiplyAdd(left, right, addend); /// @@ -859,21 +867,27 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destinati // /// + [Intrinsic] public static double Acosh(double x) => Math.Acosh(x); /// + [Intrinsic] public static double Asinh(double x) => Math.Asinh(x); /// + [Intrinsic] public static double Atanh(double x) => Math.Atanh(x); /// + [Intrinsic] public static double Cosh(double x) => Math.Cosh(x); /// + [Intrinsic] public static double Sinh(double x) => Math.Sinh(x); /// + [Intrinsic] public static double Tanh(double x) => Math.Tanh(x); // @@ -888,6 +902,7 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destinati // /// + [Intrinsic] public static double Log(double x) => Math.Log(x); /// @@ -900,6 +915,7 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destinati public static double Log2P1(double x) => Math.Log2(x + 1); /// + [Intrinsic] public static double Log10(double x) => Math.Log10(x); /// @@ -947,9 +963,11 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destinati public static double CopySign(double value, double sign) => Math.CopySign(value, sign); /// + [Intrinsic] public static double Max(double x, double y) => Math.Max(x, y); /// + [Intrinsic] public static double MaxNumber(double x, double y) { // This matches the IEEE 754:2019 `maximumNumber` function @@ -972,9 +990,11 @@ public static double MaxNumber(double x, double y) } /// + [Intrinsic] public static double Min(double x, double y) => Math.Min(x, y); /// + [Intrinsic] public static double MinNumber(double x, double y) { // This matches the IEEE 754:2019 `minimumNumber` function @@ -1013,6 +1033,7 @@ public static double MinNumber(double x, double y) static double INumberBase.Zero => Zero; /// + [Intrinsic] public static double Abs(double value) => Math.Abs(value); /// @@ -1108,9 +1129,11 @@ public static bool IsRealNumber(double value) static bool INumberBase.IsZero(double value) => (value == 0); /// + [Intrinsic] public static double MaxMagnitude(double x, double y) => Math.MaxMagnitude(x, y); /// + [Intrinsic] public static double MaxMagnitudeNumber(double x, double y) { // This matches the IEEE 754:2019 `maximumMagnitudeNumber` function @@ -1136,9 +1159,11 @@ public static double MaxMagnitudeNumber(double x, double y) } /// + [Intrinsic] public static double MinMagnitude(double x, double y) => Math.MinMagnitude(x, y); /// + [Intrinsic] public static double MinMagnitudeNumber(double x, double y) { // This matches the IEEE 754:2019 `minimumMagnitudeNumber` function @@ -1429,6 +1454,7 @@ private static bool TryConvertTo(double value, [MaybeNullWhen(false)] ou // /// + [Intrinsic] public static double Pow(double x, double y) => Math.Pow(x, y); // @@ -1436,6 +1462,7 @@ private static bool TryConvertTo(double value, [MaybeNullWhen(false)] ou // /// + [Intrinsic] public static double Cbrt(double x) => Math.Cbrt(x); /// @@ -1719,6 +1746,7 @@ static double NegativeN(double x, int n) } /// + [Intrinsic] public static double Sqrt(double x) => Math.Sqrt(x); // @@ -1750,6 +1778,7 @@ static double NegativeN(double x, int n) // /// + [Intrinsic] public static double Acos(double x) => Math.Acos(x); /// @@ -1759,6 +1788,7 @@ public static double AcosPi(double x) } /// + [Intrinsic] public static double Asin(double x) => Math.Asin(x); /// @@ -1768,6 +1798,7 @@ public static double AsinPi(double x) } /// + [Intrinsic] public static double Atan(double x) => Math.Atan(x); /// @@ -1777,6 +1808,7 @@ public static double AtanPi(double x) } /// + [Intrinsic] public static double Cos(double x) => Math.Cos(x); /// @@ -1869,6 +1901,7 @@ public static double CosPi(double x) } /// + [Intrinsic] public static double Sin(double x) => Math.Sin(x); /// @@ -2076,6 +2109,7 @@ public static double SinPi(double x) } /// + [Intrinsic] public static double Tan(double x) => Math.Tan(x); /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Math.cs b/src/libraries/System.Private.CoreLib/src/System/Math.cs index 5c7252bfefffe3..db55090f80f84a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Math.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Math.cs @@ -994,6 +994,7 @@ public static nuint Max(nuint val1, nuint val2) return (val1 >= val2) ? val1 : val2; } + [Intrinsic] public static double MaxMagnitude(double x, double y) { // This matches the IEEE 754:2019 `maximumMagnitude` function @@ -1143,6 +1144,7 @@ public static nuint Min(nuint val1, nuint val2) return (val1 <= val2) ? val1 : val2; } + [Intrinsic] public static double MinMagnitude(double x, double y) { // This matches the IEEE 754:2019 `minimumMagnitude` function diff --git a/src/libraries/System.Private.CoreLib/src/System/MathF.cs b/src/libraries/System.Private.CoreLib/src/System/MathF.cs index 31ae24f93e92d1..17f466bae27908 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MathF.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MathF.cs @@ -47,6 +47,7 @@ public static partial class MathF private const int ILogB_Zero = (-1 - 0x7fffffff); + [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Abs(float x) { @@ -241,12 +242,14 @@ public static float Log(float x, float y) return Log(x) / Log(y); } + [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Max(float x, float y) { return Math.Max(x, y); } + [Intrinsic] public static float MaxMagnitude(float x, float y) { // This matches the IEEE 754:2019 `maximumMagnitude` function @@ -271,12 +274,14 @@ public static float MaxMagnitude(float x, float y) return y; } + [Intrinsic] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Min(float x, float y) { return Math.Min(x, y); } + [Intrinsic] public static float MinMagnitude(float x, float y) { // This matches the IEEE 754:2019 `minimumMagnitude` function diff --git a/src/libraries/System.Private.CoreLib/src/System/Single.cs b/src/libraries/System.Private.CoreLib/src/System/Single.cs index 42d63de43279b7..42f5861c3be712 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Single.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Single.cs @@ -563,6 +563,7 @@ public static bool IsPow2(float value) } /// + [Intrinsic] public static float Log2(float value) => MathF.Log2(value); // @@ -616,6 +617,7 @@ public static bool IsPow2(float value) // /// + [Intrinsic] public static float Exp(float x) => MathF.Exp(x); /// @@ -638,12 +640,15 @@ public static bool IsPow2(float value) // /// + [Intrinsic] public static float Ceiling(float x) => MathF.Ceiling(x); /// + [Intrinsic] public static float Floor(float x) => MathF.Floor(x); /// + [Intrinsic] public static float Round(float x) => MathF.Round(x); /// @@ -656,6 +661,7 @@ public static bool IsPow2(float value) public static float Round(float x, int digits, MidpointRounding mode) => MathF.Round(x, digits, mode); /// + [Intrinsic] public static float Truncate(float x) => MathF.Truncate(x); /// @@ -799,6 +805,7 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destinatio static float IFloatingPointIeee754.PositiveInfinity => PositiveInfinity; /// + [Intrinsic] public static float Atan2(float y, float x) => MathF.Atan2(y, x); /// @@ -811,6 +818,7 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destinatio public static float BitIncrement(float x) => MathF.BitIncrement(x); /// + [Intrinsic] public static float FusedMultiplyAdd(float left, float right, float addend) => MathF.FusedMultiplyAdd(left, right, addend); /// @@ -839,21 +847,27 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destinatio // /// + [Intrinsic] public static float Acosh(float x) => MathF.Acosh(x); /// + [Intrinsic] public static float Asinh(float x) => MathF.Asinh(x); /// + [Intrinsic] public static float Atanh(float x) => MathF.Atanh(x); /// + [Intrinsic] public static float Cosh(float x) => MathF.Cosh(x); /// + [Intrinsic] public static float Sinh(float x) => MathF.Sinh(x); /// + [Intrinsic] public static float Tanh(float x) => MathF.Tanh(x); // @@ -868,6 +882,7 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destinatio // /// + [Intrinsic] public static float Log(float x) => MathF.Log(x); /// @@ -877,6 +892,7 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destinatio public static float LogP1(float x) => MathF.Log(x + 1); /// + [Intrinsic] public static float Log10(float x) => MathF.Log10(x); /// @@ -927,9 +943,11 @@ bool IFloatingPoint.TryWriteSignificandLittleEndian(Span destinatio public static float CopySign(float value, float sign) => MathF.CopySign(value, sign); /// + [Intrinsic] public static float Max(float x, float y) => MathF.Max(x, y); /// + [Intrinsic] public static float MaxNumber(float x, float y) { // This matches the IEEE 754:2019 `maximumNumber` function @@ -952,9 +970,11 @@ public static float MaxNumber(float x, float y) } /// + [Intrinsic] public static float Min(float x, float y) => MathF.Min(x, y); /// + [Intrinsic] public static float MinNumber(float x, float y) { // This matches the IEEE 754:2019 `minimumNumber` function @@ -993,6 +1013,7 @@ public static float MinNumber(float x, float y) static float INumberBase.Zero => Zero; /// + [Intrinsic] public static float Abs(float value) => MathF.Abs(value); /// @@ -1088,9 +1109,11 @@ public static bool IsRealNumber(float value) static bool INumberBase.IsZero(float value) => (value == 0); /// + [Intrinsic] public static float MaxMagnitude(float x, float y) => MathF.MaxMagnitude(x, y); /// + [Intrinsic] public static float MaxMagnitudeNumber(float x, float y) { // This matches the IEEE 754:2019 `maximumMagnitudeNumber` function @@ -1116,9 +1139,11 @@ public static float MaxMagnitudeNumber(float x, float y) } /// + [Intrinsic] public static float MinMagnitude(float x, float y) => MathF.MinMagnitude(x, y); /// + [Intrinsic] public static float MinMagnitudeNumber(float x, float y) { // This matches the IEEE 754:2019 `minimumMagnitudeNumber` function @@ -1409,6 +1434,7 @@ private static bool TryConvertTo(float value, [MaybeNullWhen(false)] out // /// + [Intrinsic] public static float Pow(float x, float y) => MathF.Pow(x, y); // @@ -1416,6 +1442,7 @@ private static bool TryConvertTo(float value, [MaybeNullWhen(false)] out // /// + [Intrinsic] public static float Cbrt(float x) => MathF.Cbrt(x); /// @@ -1596,6 +1623,7 @@ static float NegativeN(float x, int n) } /// + [Intrinsic] public static float Sqrt(float x) => MathF.Sqrt(x); // @@ -1627,6 +1655,7 @@ static float NegativeN(float x, int n) // /// + [Intrinsic] public static float Acos(float x) => MathF.Acos(x); /// @@ -1636,6 +1665,7 @@ public static float AcosPi(float x) } /// + [Intrinsic] public static float Asin(float x) => MathF.Asin(x); /// @@ -1645,6 +1675,7 @@ public static float AsinPi(float x) } /// + [Intrinsic] public static float Atan(float x) => MathF.Atan(x); /// @@ -1654,6 +1685,7 @@ public static float AtanPi(float x) } /// + [Intrinsic] public static float Cos(float x) => MathF.Cos(x); /// @@ -1746,6 +1778,7 @@ public static float CosPi(float x) } /// + [Intrinsic] public static float Sin(float x) => MathF.Sin(x); /// @@ -1953,6 +1986,7 @@ public static float SinPi(float x) } /// + [Intrinsic] public static float Tan(float x) => MathF.Tan(x); ///