From fb9e3c2ae9fcf908fb194e3668035126b0d9af84 Mon Sep 17 00:00:00 2001 From: Sebastian Nickolls Date: Fri, 3 Oct 2025 12:38:18 +0000 Subject: [PATCH 1/4] Remove 128-bit limit on Vector size for ARM64 If InstructionSet_VectorT is available, set the class instance size to the process SVE vector length. Increase the maximum bound in structMightRepresentSIMDType to allow the JIT to detect this when the ISA is present. --- src/coreclr/jit/compiler.h | 7 ++++++- src/coreclr/jit/targetarm64.h | 3 +++ src/coreclr/vm/codeman.cpp | 10 ++++++---- src/coreclr/vm/methodtablebuilder.cpp | 15 +++++++++++++-- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 15161eacd678d8..b9f41aaada5068 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -10599,7 +10599,12 @@ class Compiler bool structSizeMightRepresentSIMDType(size_t structSize) { #ifdef FEATURE_SIMD - return (structSize >= getMinVectorByteLength()) && (structSize <= getMaxVectorByteLength()); +#ifdef TARGET_ARM64 + const uint32_t max = compExactlyDependsOn(InstructionSet_VectorT) ? MAX_SVE_REGSIZE_BYTES : FP_REGSIZE_BYTES; +#else + const uint32_t max = getMaxVectorByteLength(); +#endif // TARGET_ARM64 + return (structSize >= getMinVectorByteLength()) && (structSize <= max); #else return false; #endif // FEATURE_SIMD diff --git a/src/coreclr/jit/targetarm64.h b/src/coreclr/jit/targetarm64.h index 1dea19fa87c0b9..bea112f319f5d6 100644 --- a/src/coreclr/jit/targetarm64.h +++ b/src/coreclr/jit/targetarm64.h @@ -370,4 +370,7 @@ #define REG_UNKBASE REG_R19 #define RBM_UNKBASE RBM_R19 + +#define MAX_SVE_REGSIZE_BYTES 256 + // clang-format on diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index 438b513b3aaf4b..254007dce44d19 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -1720,10 +1720,12 @@ void EEJitManager::SetCpuInfo() uint32_t maxVectorTLength = (maxVectorTBitWidth / 8); uint64_t sveLengthFromOS = GetSveLengthFromOS(); - // For now, enable SVE only when the system vector length is 16 bytes (128-bits) - // TODO: https://github.com/dotnet/runtime/issues/101477 - if (sveLengthFromOS == 16) - // if ((maxVectorTLength >= sveLengthFromOS) || (maxVectorTBitWidth == 0)) + if (sveLengthFromOS == 16 +#ifdef _DEBUG + || (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_JitUseScalableVectorT) + && ((maxVectorTLength >= sveLengthFromOS) || (maxVectorTBitWidth == 0))) +#endif + ) { CPUCompileFlags.Set(InstructionSet_Sve); diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index fb16290b29e561..c9984deef79a1b 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -1174,6 +1174,10 @@ MethodTableBuilder::CopyParentVtable() } } +#ifdef TARGET_ARM64 +extern "C" uint64_t GetSveLengthFromOS(); +#endif + //******************************************************************************* // Determine if this is the special SIMD type System.Numerics.Vector, whose // size is determined dynamically based on the hardware and the presence of JIT @@ -1186,7 +1190,7 @@ BOOL MethodTableBuilder::CheckIfSIMDAndUpdateSize() { STANDARD_VM_CONTRACT; -#if defined(TARGET_X86) || defined(TARGET_AMD64) +#if defined(TARGET_X86) || defined(TARGET_AMD64) || defined(TARGET_ARM64) if (!bmtProp->fIsIntrinsicType) return false; @@ -1205,6 +1209,7 @@ BOOL MethodTableBuilder::CheckIfSIMDAndUpdateSize() CORJIT_FLAGS CPUCompileFlags = ExecutionManager::GetEEJitManager()->GetCPUCompileFlags(); uint32_t numInstanceFieldBytes = 16; +#if defined(TARGET_X86) || defined(TARGET_AMD64) if (CPUCompileFlags.IsSet(InstructionSet_VectorT512)) { numInstanceFieldBytes = 64; @@ -1213,13 +1218,19 @@ BOOL MethodTableBuilder::CheckIfSIMDAndUpdateSize() { numInstanceFieldBytes = 32; } +#elif defined(TARGET_ARM64) + if (CPUCompileFlags.IsSet(InstructionSet_VectorT)) + { + numInstanceFieldBytes = (uint32_t) GetSveLengthFromOS(); + } +#endif if (numInstanceFieldBytes != 16) { bmtFP->NumInstanceFieldBytes = numInstanceFieldBytes; return true; } -#endif // TARGET_X86 || TARGET_AMD64 +#endif // TARGET_X86 || TARGET_AMD64 || TARGET_ARM64 return false; } From aa84d2521d5d7fddb54801ca8d0f6af5cb652ed0 Mon Sep 17 00:00:00 2001 From: Sebastian Nickolls Date: Mon, 29 Jun 2026 08:41:57 +0000 Subject: [PATCH 2/4] Refactor structSizeMightRepresentSIMDType -> structMightRepresentSIMDType Refactor the function to take in the class handle and query flag and size properties from the handle. Use the flag properties to optimize the function. --- src/coreclr/jit/compiler.cpp | 2 +- src/coreclr/jit/compiler.h | 13 ++++++++++++- src/coreclr/jit/importer.cpp | 30 +++++++++++------------------- src/coreclr/jit/lclvars.cpp | 2 +- src/coreclr/jit/morph.cpp | 2 +- src/coreclr/jit/simd.cpp | 2 +- src/coreclr/jit/valuenum.cpp | 7 +------ 7 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index c992f86b585568..9b72b03b86d13d 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -984,7 +984,7 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, // TODO-SVE: For now, we always pass Vector by reference. Support passing Vector in Z registers. unsigned simdSize = 0; - if (structSizeMightRepresentSIMDType(structSize) && + if (structMightRepresentSIMDType(clsHnd) && (getBaseTypeAndSizeOfSIMDType(clsHnd, &simdSize) != TYP_UNDEF) && (simdSize == SIZE_UNKNOWN)) { howToReturnStruct = SPK_ByReference; diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index b9f41aaada5068..5a4ac44edfa80b 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -10596,8 +10596,19 @@ class Compiler // Use to determine if a struct *might* be a SIMD type. As this function only takes a size, many // structs will fit the criteria. - bool structSizeMightRepresentSIMDType(size_t structSize) + bool structMightRepresentSIMDType(CORINFO_CLASS_HANDLE clsHnd) { + uint32_t structFlags = info.compCompHnd->getClassAttribs(clsHnd); + uint32_t filteredFlags = structFlags & (CORINFO_FLG_VALUECLASS | CORINFO_FLG_CONTAINS_GC_PTR | + CORINFO_FLG_BYREF_LIKE | CORINFO_FLG_INTRINSIC_TYPE); + uint32_t desiredFlags = (CORINFO_FLG_VALUECLASS | CORINFO_FLG_INTRINSIC_TYPE); + + if (filteredFlags != desiredFlags) + { + return false; + } + + unsigned structSize = info.compCompHnd->getClassSize(clsHnd); #ifdef FEATURE_SIMD #ifdef TARGET_ARM64 const uint32_t max = compExactlyDependsOn(InstructionSet_VectorT) ? MAX_SVE_REGSIZE_BYTES : FP_REGSIZE_BYTES; diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index f521bbfcaa6a3e..508440ae0ebda1 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -1154,7 +1154,7 @@ GenTree* Compiler::impGetNodeAddr(GenTree* val, // Normalizing the type involves examining the struct type to determine if it should // be modified to one that is handled specially by the JIT, possibly being a candidate // for full enregistration, e.g. TYP_SIMD16. If the size of the struct is already known -// call structSizeMightRepresentSIMDType to determine if this api needs to be called. +// call structMightRepresentSIMDType to determine if this api needs to be called. // var_types Compiler::impNormStructType(CORINFO_CLASS_HANDLE structHnd, var_types* pSimdBaseJitType) { @@ -1163,28 +1163,20 @@ var_types Compiler::impNormStructType(CORINFO_CLASS_HANDLE structHnd, var_types* var_types structType = TYP_STRUCT; #ifdef FEATURE_SIMD - const DWORD structFlags = info.compCompHnd->getClassAttribs(structHnd); - - // Don't bother if the struct contains GC references of byrefs, it can't be a SIMD type. - if ((structFlags & (CORINFO_FLG_CONTAINS_GC_PTR | CORINFO_FLG_BYREF_LIKE)) == 0) + if (structMightRepresentSIMDType(structHnd)) { - unsigned originalSize = info.compCompHnd->getClassSize(structHnd); - - if (structSizeMightRepresentSIMDType(originalSize)) + unsigned int sizeBytes; + var_types simdBaseType = getBaseTypeAndSizeOfSIMDType(structHnd, &sizeBytes); + if (simdBaseType != TYP_UNDEF) { - unsigned int sizeBytes; - var_types simdBaseType = getBaseTypeAndSizeOfSIMDType(structHnd, &sizeBytes); - if (simdBaseType != TYP_UNDEF) + assert((sizeBytes == info.compCompHnd->getClassSize(structHnd)) || (sizeBytes == SIZE_UNKNOWN)); + structType = getSIMDTypeForSize(sizeBytes); + if (pSimdBaseJitType != nullptr) { - assert(sizeBytes == originalSize || sizeBytes == SIZE_UNKNOWN); - structType = getSIMDTypeForSize(sizeBytes); - if (pSimdBaseJitType != nullptr) - { - *pSimdBaseJitType = simdBaseType; - } - // Also indicate that we use floating point registers. - compFloatingPointUsed = true; + *pSimdBaseJitType = simdBaseType; } + // Also indicate that we use floating point registers. + compFloatingPointUsed = true; } } #endif // FEATURE_SIMD diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 5ee0d44d8d374f..030792595b7480 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -1615,7 +1615,7 @@ var_types Compiler::StructPromotionHelper::TryPromoteValueClassAsPrimitive(CORIN // We will only promote fields of SIMD types that fit into a SIMD register. if (simdBaseType != TYP_UNDEF) { - if (m_compiler->structSizeMightRepresentSIMDType(simdSize)) + if (m_compiler->structMightRepresentSIMDType(node.simdTypeHnd)) { return m_compiler->getSIMDTypeForSize(simdSize); } diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 36f5be8c776ceb..08224936faeecd 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -2890,7 +2890,7 @@ GenTree* Compiler::fgMorphIndexAddr(GenTreeIndexAddr* indexAddr) } #ifdef FEATURE_SIMD - if (varTypeIsStruct(elemTyp) && structSizeMightRepresentSIMDType(elemSize)) + if (varTypeIsStruct(elemTyp)) { elemTyp = impNormStructType(elemStructType); } diff --git a/src/coreclr/jit/simd.cpp b/src/coreclr/jit/simd.cpp index 79469cc05890b1..88b6b2b65eae7c 100644 --- a/src/coreclr/jit/simd.cpp +++ b/src/coreclr/jit/simd.cpp @@ -173,7 +173,7 @@ var_types Compiler::getBaseTypeForPrimitiveNumericClass(CORINFO_CLASS_HANDLE cls // sizeBytes if non-null is set to size in bytes. // // Notes: -// If the size of the struct is already known call structSizeMightRepresentSIMDType +// If the size of the struct is already known call structMightRepresentSIMDType // to determine if this api needs to be called. // // The type handle passed here can only be used in a subset of JIT-EE calls diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index a8ee6dd94e0a8b..b050e446e225e8 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -3769,12 +3769,7 @@ ValueNum ValueNumStore::VNForFieldSelector(CORINFO_FIELD_HANDLE fieldHnd, var_ty if (fieldType == TYP_STRUCT) { structSize = m_compiler->info.compCompHnd->getClassSize(structHnd); - - // We have to normalize here since there is no CorInfoType for vectors... - if (m_compiler->structSizeMightRepresentSIMDType(structSize)) - { - fieldType = m_compiler->impNormStructType(structHnd); - } + fieldType = m_compiler->impNormStructType(structHnd); } *pFieldType = fieldType; From 42b7df07ef3de0f9f99d2466dd2f6ebfe3a11ace Mon Sep 17 00:00:00 2001 From: Sebastian Nickolls Date: Mon, 29 Jun 2026 15:14:08 +0000 Subject: [PATCH 3/4] Use compOpportunisticallyDependsOn --- src/coreclr/jit/compiler.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 5a4ac44edfa80b..bcb70d8e04706b 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -10611,7 +10611,8 @@ class Compiler unsigned structSize = info.compCompHnd->getClassSize(clsHnd); #ifdef FEATURE_SIMD #ifdef TARGET_ARM64 - const uint32_t max = compExactlyDependsOn(InstructionSet_VectorT) ? MAX_SVE_REGSIZE_BYTES : FP_REGSIZE_BYTES; + const uint32_t max = + compOpportunisticallyDependsOn(InstructionSet_VectorT) ? MAX_SVE_REGSIZE_BYTES : FP_REGSIZE_BYTES; #else const uint32_t max = getMaxVectorByteLength(); #endif // TARGET_ARM64 From 0488bcef332e50005709f5ddd5acafc092f6e6b9 Mon Sep 17 00:00:00 2001 From: Sebastian Nickolls Date: Tue, 30 Jun 2026 08:10:02 +0000 Subject: [PATCH 4/4] Update documentation for structMightRepresentSIMDType --- src/coreclr/jit/compiler.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index bcb70d8e04706b..e39caf4460a27e 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -10594,8 +10594,15 @@ class Compiler return threshold; } - // Use to determine if a struct *might* be a SIMD type. As this function only takes a size, many - // structs will fit the criteria. + //--------------------------------------------------------------------------------------------- + // structMightRepresentSIMDType: Can this class handle be a SIMD type? + // + // Returns: + // false if it is not possible for this class handle to be a SIMD type, otherwise true. + // + // Notes: + // SIMD types are currently all value classes annotated with the [Intrinsic] attribute. + // This is a first stage filter, caller should verify exact SIMD types by name. bool structMightRepresentSIMDType(CORINFO_CLASS_HANDLE clsHnd) { uint32_t structFlags = info.compCompHnd->getClassAttribs(clsHnd);