@@ -813,33 +813,10 @@ private static Vector128<byte> IndexOfAnyLookup<TNegator, TOptimizations>(Vector
813813 where TNegator : struct , INegator
814814 where TOptimizations : struct , IOptimizations
815815 {
816- // Pack two vectors of characters into bytes. While the type is Vector128<short>, these are really UInt16 characters.
817- // X86 and WASM: Downcast every character using saturation.
818- // - Values <= 32767 result in min(value, 255).
819- // - Values > 32767 result in 0. Because of this we must do more work to handle needles that contain 0.
820- // ARM64: Do narrowing saturation over unsigned values.
821- // - All values result in min(value, 255)
822- Vector128 < byte > source =
823- Sse2 . IsSupported ? Sse2 . PackUnsignedSaturate ( source0 , source1 ) :
824- AdvSimd . IsSupported ? AdvSimd . ExtractNarrowingSaturateUpper ( AdvSimd . ExtractNarrowingSaturateLower ( source0 . AsUInt16 ( ) ) , source1 . AsUInt16 ( ) ) :
825- PackedSimd . ConvertNarrowingUnsignedSaturate ( source0 , source1 ) ;
816+ Vector128 < byte > source = TOptimizations . PackSources ( source0 . AsUInt16 ( ) , source1 . AsUInt16 ( ) ) ;
826817
827818 Vector128 < byte > result = IndexOfAnyLookupCore ( source , bitmapLookup ) ;
828819
829- // On X86 and WASM, the packing/narrowing above resulted in values becoming 0 for inputs above 32767.
830- // Any value above 32767 would therefore match against 0. If 0 is present in the needle, we must clear the false positives.
831- // We can correct the result by clearing any bits that matched with a non-ascii source character.
832- if ( TOptimizations . NeedleContainsZero )
833- {
834- Debug . Assert ( Sse2 . IsSupported || PackedSimd . IsSupported ) ;
835- Vector128 < short > ascii0 = Vector128 . LessThan ( source0 . AsUInt16 ( ) , Vector128 . Create ( ( ushort ) 128 ) ) . AsInt16 ( ) ;
836- Vector128 < short > ascii1 = Vector128 . LessThan ( source1 . AsUInt16 ( ) , Vector128 . Create ( ( ushort ) 128 ) ) . AsInt16 ( ) ;
837- Vector128 < byte > ascii = Sse2 . IsSupported
838- ? Sse2 . PackSignedSaturate ( ascii0 , ascii1 ) . AsByte ( )
839- : PackedSimd . ConvertNarrowingSignedSaturate ( ascii0 , ascii1 ) . AsByte ( ) ;
840- result &= ascii ;
841- }
842-
843820 return TNegator . NegateIfNeeded ( result ) ;
844821 }
845822
@@ -870,23 +847,14 @@ private static Vector128<byte> IndexOfAnyLookupCore(Vector128<byte> source, Vect
870847 return result ;
871848 }
872849
873- [ BypassReadyToRun ]
874850 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
875851 private static Vector256 < byte > IndexOfAnyLookup < TNegator , TOptimizations > ( Vector256 < short > source0 , Vector256 < short > source1 , Vector256 < byte > bitmapLookup )
876852 where TNegator : struct , INegator
877853 where TOptimizations : struct , IOptimizations
878854 {
879- // See comments in IndexOfAnyLookup(Vector128<byte>) above for more details.
880- Vector256 < byte > source = Avx2 . PackUnsignedSaturate ( source0 , source1 ) ;
881- Vector256 < byte > result = IndexOfAnyLookupCore ( source , bitmapLookup ) ;
855+ Vector256 < byte > source = TOptimizations . PackSources ( source0 . AsUInt16 ( ) , source1 . AsUInt16 ( ) ) ;
882856
883- if ( TOptimizations . NeedleContainsZero )
884- {
885- Vector256 < short > ascii0 = Vector256 . LessThan ( source0 . AsUInt16 ( ) , Vector256 . Create ( ( ushort ) 128 ) ) . AsInt16 ( ) ;
886- Vector256 < short > ascii1 = Vector256 . LessThan ( source1 . AsUInt16 ( ) , Vector256 . Create ( ( ushort ) 128 ) ) . AsInt16 ( ) ;
887- Vector256 < byte > ascii = Avx2 . PackSignedSaturate ( ascii0 , ascii1 ) . AsByte ( ) ;
888- result &= ascii ;
889- }
857+ Vector256 < byte > result = IndexOfAnyLookupCore ( source , bitmapLookup ) ;
890858
891859 return TNegator . NegateIfNeeded ( result ) ;
892860 }
@@ -1115,17 +1083,58 @@ internal interface INegator
11151083
11161084 internal interface IOptimizations
11171085 {
1118- static abstract bool NeedleContainsZero { get ; }
1086+ // Pack two vectors of characters into bytes.
1087+ // X86 and WASM when the needle does not contain 0: Downcast every character using saturation.
1088+ // - Values <= 32767 result in min(value, 255).
1089+ // - Values > 32767 result in 0. Because of this we must do more work to handle needles that contain 0.
1090+ // Otherwise: Do narrowing saturation over unsigned values.
1091+ // - All values result in min(value, 255)
1092+ static abstract Vector128 < byte > PackSources ( Vector128 < ushort > lower , Vector128 < ushort > upper ) ;
1093+ static abstract Vector256 < byte > PackSources ( Vector256 < ushort > lower , Vector256 < ushort > upper ) ;
11191094 }
11201095
11211096 internal readonly struct Ssse3AndWasmHandleZeroInNeedle : IOptimizations
11221097 {
1123- public static bool NeedleContainsZero => true ;
1098+ // Replace with Vector128.NarrowWithSaturation once https://github.com/dotnet/runtime/issues/75724 is implemented.
1099+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
1100+ public static Vector128 < byte > PackSources ( Vector128 < ushort > lower , Vector128 < ushort > upper )
1101+ {
1102+ Vector128 < short > lowerMin = Vector128 . Min ( lower , Vector128 . Create ( ( ushort ) 255 ) ) . AsInt16 ( ) ;
1103+ Vector128 < short > upperMin = Vector128 . Min ( upper , Vector128 . Create ( ( ushort ) 255 ) ) . AsInt16 ( ) ;
1104+
1105+ return Sse2 . IsSupported
1106+ ? Sse2 . PackUnsignedSaturate ( lowerMin , upperMin )
1107+ : PackedSimd . ConvertNarrowingUnsignedSaturate ( lowerMin , upperMin ) ;
1108+ }
1109+
1110+ // Replace with Vector256.NarrowWithSaturation once https://github.com/dotnet/runtime/issues/75724 is implemented.
1111+ [ BypassReadyToRun ]
1112+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
1113+ public static Vector256 < byte > PackSources ( Vector256 < ushort > lower , Vector256 < ushort > upper )
1114+ {
1115+ return Avx2 . PackUnsignedSaturate (
1116+ Vector256 . Min ( lower , Vector256 . Create ( ( ushort ) 255 ) ) . AsInt16 ( ) ,
1117+ Vector256 . Min ( upper , Vector256 . Create ( ( ushort ) 255 ) ) . AsInt16 ( ) ) ;
1118+ }
11241119 }
11251120
11261121 internal readonly struct Default : IOptimizations
11271122 {
1128- public static bool NeedleContainsZero => false ;
1123+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
1124+ public static Vector128 < byte > PackSources ( Vector128 < ushort > lower , Vector128 < ushort > upper )
1125+ {
1126+ return
1127+ Sse2 . IsSupported ? Sse2 . PackUnsignedSaturate ( lower . AsInt16 ( ) , upper . AsInt16 ( ) ) :
1128+ AdvSimd . IsSupported ? AdvSimd . ExtractNarrowingSaturateUpper ( AdvSimd . ExtractNarrowingSaturateLower ( lower ) , upper ) :
1129+ PackedSimd . ConvertNarrowingUnsignedSaturate ( lower . AsInt16 ( ) , upper . AsInt16 ( ) ) ;
1130+ }
1131+
1132+ [ BypassReadyToRun ]
1133+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
1134+ public static Vector256 < byte > PackSources ( Vector256 < ushort > lower , Vector256 < ushort > upper )
1135+ {
1136+ return Avx2 . PackUnsignedSaturate ( lower . AsInt16 ( ) , upper . AsInt16 ( ) ) ;
1137+ }
11291138 }
11301139 }
11311140}
0 commit comments