diff --git a/src/coreclr/inc/cor.h b/src/coreclr/inc/cor.h index 4b9e9fe6953f02..ad333d1dee5060 100644 --- a/src/coreclr/inc/cor.h +++ b/src/coreclr/inc/cor.h @@ -1665,6 +1665,14 @@ FORCEINLINE int CorIsPrimitiveType(CorElementType elementtype) return (elementtype < ELEMENT_TYPE_PTR || elementtype == ELEMENT_TYPE_I || elementtype == ELEMENT_TYPE_U); } +// Returns true if the element type is a real or integer type. +FORCEINLINE bool CorIsNumericalType(CorElementType elementType) +{ + return ((elementType >= ELEMENT_TYPE_I1) && (elementType <= ELEMENT_TYPE_R8)) + || (elementType == ELEMENT_TYPE_I) + || (elementType == ELEMENT_TYPE_U); +} + // Return true if element type is a modifier, i.e. ELEMENT_TYPE_MODIFIER bits are // turned on. For now, it is checking for ELEMENT_TYPE_PTR and ELEMENT_TYPE_BYREF diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index daf91aaf30e983..01e58622db61f1 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -982,9 +982,17 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, // Structs that are pointer sized or smaller should have been handled by getPrimitiveTypeForStruct assert(structSize > TARGET_POINTER_SIZE); + // TODO-SVE: For now, we always pass Vector by reference. Support passing Vector in Z registers. + unsigned simdSize = 0; + if (structSizeMightRepresentSIMDType(structSize) && + (getBaseTypeAndSizeOfSIMDType(clsHnd, &simdSize) != TYP_UNDEF) && (simdSize == SIZE_UNKNOWN)) + { + howToReturnStruct = SPK_ByReference; + useType = TYP_UNKNOWN; + } // On ARM64 structs that are 9-16 bytes are returned by value in multiple registers // - if (structSize <= (TARGET_POINTER_SIZE * 2)) + else if (structSize <= (TARGET_POINTER_SIZE * 2)) { // setup wbPassType and useType indicate that this is return by value in multiple registers howToReturnStruct = SPK_ByValue; diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 166e4a1097e8f0..fbae8273f705fe 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -4607,7 +4607,7 @@ GenTree::VisitResult GenTree::VisitLocalDefs(Compiler* comp, TVisitor visitor) { unsigned storeSize = comp->typGetObjLayout(AsCall()->gtRetClsHnd)->GetSize(); - bool isEntire = storeSize == comp->lvaLclExactSize(lclAddr->GetLclNum()); + bool isEntire = comp->IsEntireAccess(lclAddr->GetLclNum(), lclAddr->GetLclOffs(), ValueSize(storeSize)); return visitor(LocalDef(lclAddr, isEntire, lclAddr->GetLclOffs(), ValueSize(storeSize))); } diff --git a/src/coreclr/jit/lclmorph.cpp b/src/coreclr/jit/lclmorph.cpp index 8f86759cdd284d..2e7e566ea8f15e 100644 --- a/src/coreclr/jit/lclmorph.cpp +++ b/src/coreclr/jit/lclmorph.cpp @@ -1526,8 +1526,9 @@ class LocalAddressVisitor final : public GenTreeVisitor callUser->gtCallMoreFlags |= GTF_CALL_M_RETBUFFARG_LCLOPT; defFlag = GTF_VAR_DEF; - if ((val.Offset() != 0) || - (varDsc->lvExactSize() != m_compiler->typGetObjLayout(callUser->gtRetClsHnd)->GetSize())) + unsigned storeSize = m_compiler->typGetObjLayout(callUser->gtRetClsHnd)->GetSize(); + + if (!m_compiler->IsEntireAccess(lclNum, val.Offset(), ValueSize(storeSize))) { defFlag |= GTF_VAR_USEASG; } diff --git a/src/coreclr/jit/targetarm64.cpp b/src/coreclr/jit/targetarm64.cpp index 244ffddaaff231..a187841146a27f 100644 --- a/src/coreclr/jit/targetarm64.cpp +++ b/src/coreclr/jit/targetarm64.cpp @@ -57,6 +57,8 @@ ABIPassingInformation Arm64Classifier::Classify(Compiler* comp, ClassLayout* structLayout, WellKnownArg wellKnownParam) { + assert(!varTypeIsMask(type)); + if ((wellKnownParam == WellKnownArg::RetBuffer) && hasFixedRetBuffReg(m_info.CallConv)) { return ABIPassingInformation::FromSegmentByValue(comp, ABIPassingSegment::InRegister(REG_ARG_RET_BUFF, 0, @@ -108,7 +110,8 @@ ABIPassingInformation Arm64Classifier::Classify(Compiler* comp, if (varTypeIsStruct(type)) { unsigned size = structLayout->GetSize(); - if (size > 16) + // TODO-SVE: We should be able to pass in a Z register. + if (size > 16 || (type == TYP_SIMD)) { passedByRef = true; slots = 1; @@ -151,7 +154,7 @@ ABIPassingInformation Arm64Classifier::Classify(Compiler* comp, // In varargs methods (only supported on Windows) all parameters go in // integer registers. - if (varTypeUsesFloatArgReg(type) && !m_info.IsVarArgs) + if (varTypeUsesFloatArgReg(type) && !m_info.IsVarArgs && !passedByRef) { regs = &m_floatRegs; } diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index 746464ca77f2c0..b6197fc3fe8c85 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -578,6 +578,12 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE #ifdef TARGET_AMD64 return IsArgPassedByRef(size); #elif defined(TARGET_ARM64) + // TODO-SVE: This should be removed when Vector has an HFA type. + if (ExecutionManager::GetEEJitManager()->UseScalableVectorT() && th.IsVectorT()) + { + return TRUE; + } + // Composites greater than 16 bytes are passed by reference return ((size > ENREGISTERED_PARAMTYPE_MAXSIZE) && !th.IsHFA()); #elif defined(TARGET_LOONGARCH64) @@ -636,7 +642,14 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE if (m_argType == ELEMENT_TYPE_VALUETYPE) { _ASSERTE(!m_argTypeHandle.IsNull()); - return ((m_argSize > ENREGISTERED_PARAMTYPE_MAXSIZE) && (!m_argTypeHandle.IsHFA() || this->IsVarArg())); + + // TODO-SVE: This should be removed when Vector has an HFA type. + if (ExecutionManager::GetEEJitManager()->UseScalableVectorT() && m_argTypeHandle.IsVectorT()) + { + return TRUE; + } + + return (((m_argSize > ENREGISTERED_PARAMTYPE_MAXSIZE) && (!m_argTypeHandle.IsHFA() || this->IsVarArg()))); } return FALSE; #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) diff --git a/src/coreclr/vm/class.cpp b/src/coreclr/vm/class.cpp index 1a37e2346b0dbe..19424b88d49569 100644 --- a/src/coreclr/vm/class.cpp +++ b/src/coreclr/vm/class.cpp @@ -1803,45 +1803,64 @@ bool MethodTable::IsHFA() #endif // !FEATURE_HFA //******************************************************************************* -int MethodTable::GetVectorSize() +CorInfoHFAElemType MethodTable::GetVectorHFA() { // This is supported for finding HVA types for Arm64. In order to support the altjit, // we support this on 64-bit platforms (i.e. Arm64 and X64). + CorInfoHFAElemType hfaType = CORINFO_HFA_ELEM_NONE; #ifdef TARGET_64BIT if (IsIntrinsicType()) { LPCUTF8 namespaceName; LPCUTF8 className = GetFullyQualifiedNameInfo(&namespaceName); - int vectorSize = 0; if (strcmp(className, "Vector`1") == 0) { _ASSERTE(strcmp(namespaceName, "System.Numerics") == 0); - vectorSize = GetNumInstanceFieldBytes(); +#ifdef TARGET_ARM64 + if (ExecutionManager::GetEEJitManager()->UseScalableVectorT()) + { + // TODO-SVE: This forces Vector to be passed by reference. Implement + // CORINFO_HFA_ELEM_VECTORT so we can pass Vector in SVE registers. + return CORINFO_HFA_ELEM_NONE; + } +#endif + switch (GetNumInstanceFieldBytes()) + { + case 8: + hfaType = CORINFO_HFA_ELEM_VECTOR64; + break; + case 16: + hfaType = CORINFO_HFA_ELEM_VECTOR128; + break; + default: + _ASSERTE(!"Invalid Vector size"); + break; + } } else if (strcmp(className, "Vector128`1") == 0) { _ASSERTE(strcmp(namespaceName, "System.Runtime.Intrinsics") == 0); - vectorSize = 16; + hfaType = CORINFO_HFA_ELEM_VECTOR128; } else if (strcmp(className, "Vector64`1") == 0) { _ASSERTE(strcmp(namespaceName, "System.Runtime.Intrinsics") == 0); - vectorSize = 8; + hfaType = CORINFO_HFA_ELEM_VECTOR64; } - if (vectorSize != 0) + + if (hfaType != CORINFO_HFA_ELEM_NONE) { - // We need to verify that T (the element or "base" type) is a primitive type. + // We need to verify that T (the element or "base" type) is a numerical type. TypeHandle typeArg = GetInstantiation()[0]; - CorElementType corType = typeArg.GetSignatureCorElementType(); - if (((corType >= ELEMENT_TYPE_I1) && (corType <= ELEMENT_TYPE_R8)) || (corType == ELEMENT_TYPE_I) || (corType == ELEMENT_TYPE_U)) + if (!CorIsNumericalType(typeArg.GetSignatureCorElementType())) { - return vectorSize; + return CORINFO_HFA_ELEM_NONE; } } } #endif // TARGET_64BIT - return 0; + return hfaType; } //******************************************************************************* @@ -1863,10 +1882,11 @@ CorInfoHFAElemType MethodTable::GetHFAType() _ASSERTE(pMT->IsValueType()); _ASSERTE(pMT->GetNumInstanceFields() > 0); - int vectorSize = pMT->GetVectorSize(); - if (vectorSize != 0) + CorInfoHFAElemType hfaType = pMT->GetVectorHFA(); + + if (hfaType != CORINFO_HFA_ELEM_NONE) { - return (vectorSize == 8) ? CORINFO_HFA_ELEM_VECTOR64 : CORINFO_HFA_ELEM_VECTOR128; + return hfaType; } PTR_FieldDesc pFirstField = pMT->GetApproxFieldDescListRaw(); @@ -1935,7 +1955,7 @@ EEClass::CheckForHFA() // The opaque Vector types appear to have multiple fields, but need to be treated // as an opaque type of a single vector. - if (GetMethodTable()->GetVectorSize() != 0) + if (GetMethodTable()->GetVectorHFA() != CORINFO_HFA_ELEM_NONE) { #if defined(FEATURE_HFA) GetMethodTable()->SetIsHFA(); @@ -1961,27 +1981,13 @@ EEClass::CheckForHFA() { case ELEMENT_TYPE_VALUETYPE: { -#ifdef TARGET_ARM64 MethodTable* pMT; #if defined(FEATURE_HFA) pMT = pByValueClassCache[i]; #else pMT = pFD->LookupApproxFieldTypeHandle().AsMethodTable(); #endif - int thisElemSize = pMT->GetVectorSize(); - if (thisElemSize != 0) - { - fieldHFAType = (thisElemSize == 8) ? CORINFO_HFA_ELEM_VECTOR64 : CORINFO_HFA_ELEM_VECTOR128; - } - else -#endif // TARGET_ARM64 - { -#if defined(FEATURE_HFA) - fieldHFAType = pByValueClassCache[i]->GetHFAType(); -#else - fieldHFAType = pFD->LookupApproxFieldTypeHandle().AsMethodTable()->GetHFAType(); -#endif - } + fieldHFAType = pMT->GetHFAType(); int requiredAlignment; switch (fieldHFAType) diff --git a/src/coreclr/vm/codeman.h b/src/coreclr/vm/codeman.h index 668b95beb9ddef..5c122f3d1f8f11 100644 --- a/src/coreclr/vm/codeman.h +++ b/src/coreclr/vm/codeman.h @@ -2252,6 +2252,14 @@ class EEJitManager final : public EECodeGenManager return m_CPUCompileFlags; } +#if defined(TARGET_ARM64) + inline bool UseScalableVectorT() + { + LIMITED_METHOD_CONTRACT; + return m_CPUCompileFlags.GetInstructionSetFlags().HasInstructionSet(InstructionSet_VectorT); + } +#endif + private : Crst m_JitLoadLock; diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index b2357f362c9410..358128ca9c167d 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -2046,8 +2046,10 @@ class MethodTable bool IsHFA(); #endif // FEATURE_HFA - // Returns the size in bytes of this type if it is a HW vector type; 0 otherwise. - int GetVectorSize(); + // Returns the HFA type for this type, if it is a valid HW vector type. + // Floating point HFA types will return CORINFO_HFA_ELEM_NONE. + // Vector classes with invalid generic parameters return CORINFO_HFA_ELEM_NONE. + CorInfoHFAElemType GetVectorHFA(); // Get the HFA type. This is supported both with FEATURE_HFA, in which case it // depends on the cached bit on the class, or without, in which case it is recomputed diff --git a/src/coreclr/vm/typehandle.cpp b/src/coreclr/vm/typehandle.cpp index 5dd7d4e85d468a..c34d3400002ab3 100644 --- a/src/coreclr/vm/typehandle.cpp +++ b/src/coreclr/vm/typehandle.cpp @@ -456,6 +456,13 @@ bool TypeHandle::IsFloatHfa() const return (GetHFAType() == CORINFO_HFA_ELEM_FLOAT); } +// Returns true when the type is Vector or any instantiation thereof. +bool TypeHandle::IsVectorT() const +{ + LIMITED_METHOD_CONTRACT; + return !IsTypeDesc() && AsMethodTable()->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__VECTORT)); +} + #ifdef FEATURE_64BIT_ALIGNMENT bool TypeHandle::RequiresAlign8() const diff --git a/src/coreclr/vm/typehandle.h b/src/coreclr/vm/typehandle.h index 1fb2f944bb509d..353fed9bb6f1c4 100644 --- a/src/coreclr/vm/typehandle.h +++ b/src/coreclr/vm/typehandle.h @@ -357,6 +357,8 @@ class TypeHandle bool IsFloatHfa() const; + bool IsVectorT() const; + #ifdef FEATURE_64BIT_ALIGNMENT bool RequiresAlign8() const; #endif // FEATURE_64BIT_ALIGNMENT