From e1f2f178820885d616cf9fbc0bc590da6fc19924 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 25 Apr 2023 15:27:43 -0700 Subject: [PATCH 01/19] Implement analyzer for platform intrinsics use in System.Private.CoreLib This analyzer detects the use of all platform intrinsics and checks to ensure that they are all used either protected by an if statement OR ternary operator which checks an appropriate IsSupported flag, or that the intrinsic is used within a method where the behavior of platform support for the intrinsic is not allowed to vary between compile time and runtime. The supported conditional checks are 1. Simple if statement checking IsSupported flag surrounding usage ``` if (PlatformIntrinsicType.IsSupported) { PlatformIntrinsicType.IntrinsicMethod(); } ``` 2. If statement check checking a platform intrinsic type which implies that the intrinsic used is supported. ``` if (Avx2.X64.IsSupported) { Avx2.IntrinsicMethod(); } ``` 3. Nested if statement where there is an outer condition which is an OR'd together series of IsSupported checks for mutually exclusive conditions and where the inner check is an else clause where some checks are excluded from applying. ``` if (Avx2.IsSupported || ArmBase.IsSupported) { if (Avx2.IsSupported) { // Do something } else { ArmBase.IntrinsicMethod(); } } ``` In addtion, this change adds a new attribute, currently named System.Runtime.BypassReadyToRunForIntrinsicsHelperUse which can be used to identify methods which are called expecting to use some particular set of platform intrinsics. These functions do not need to have explicit checks, and while this is not yet implemented in the change, the attribute will change the behavior of crossgen2 so that code will only be generated if the platform intrinsic support is well known to be either enabled or disabled at runtime. In addition, the method will only be compiled or be inlineable if the platform intrinsic support is enabled that matches at least one of the attributes applied to that method. --- .../Common/src/System/HexConverter.cs | 1 + ...ntrinsicsInSystemPrivateCoreLibAnalyzer.cs | 533 ++++++++++++++++++ .../System.Private.CoreLib.Generators.csproj | 1 + .../System.Private.CoreLib.Shared.projitems | 1 + .../src/System/Buffers/Text/Base64Decoder.cs | 3 + .../src/System/Buffers/Text/Base64Encoder.cs | 3 + .../IndexOfAnyAsciiSearcher.cs | 7 + .../IndexOfAnyValues/ProbabilisticMap.cs | 4 + .../src/System/Numerics/Matrix4x4.Impl.cs | 1 + .../src/System/Numerics/VectorMath.cs | 2 +- .../BypassReadyToRunForIntrinsicsHelperUse.cs | 18 + .../System/Runtime/Intrinsics/Vector128.cs | 8 + .../src/System/SpanHelpers.Packed.cs | 3 + .../src/System/Text/Ascii.Utility.cs | 2 + .../src/System/Text/Latin1Utility.cs | 3 + .../Text/Unicode/Utf16Utility.Validation.cs | 1 + .../Text/Unicode/Utf8Utility.Transcoding.cs | 2 +- .../Text/Unicode/Utf8Utility.Validation.cs | 1 + 18 files changed, 592 insertions(+), 2 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/Runtime/BypassReadyToRunForIntrinsicsHelperUse.cs diff --git a/src/libraries/Common/src/System/HexConverter.cs b/src/libraries/Common/src/System/HexConverter.cs index b80e404442b02c..621db00e023396 100644 --- a/src/libraries/Common/src/System/HexConverter.cs +++ b/src/libraries/Common/src/System/HexConverter.cs @@ -236,6 +236,7 @@ public static bool TryDecodeFromUtf16(ReadOnlySpan chars, Span bytes } #if SYSTEM_PRIVATE_CORELIB + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] public static bool TryDecodeFromUtf16_Vector128(ReadOnlySpan chars, Span bytes) { Debug.Assert(Ssse3.IsSupported || AdvSimd.Arm64.IsSupported); diff --git a/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs b/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs new file mode 100644 index 00000000000000..8be3fc9bda0ab2 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs @@ -0,0 +1,533 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +// This isn't a shipping analyzer, so we don't need release tracking +#pragma warning disable RS2008 + +#nullable enable + +namespace IntrinsicsInSystemPrivateCoreLib +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + [CLSCompliant(false)] + public class IntrinsicsInSystemPrivateCoreLibAnalyzer : DiagnosticAnalyzer + { + public const string DiagnosticId = "IntrinsicsInSystemPrivateCoreLib"; + + // You can change these strings in the Resources.resx file. If you do not want your analyzer to be localize-able, you can use regular strings for Title and MessageFormat. + // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/Localizing%20Analyzers.md for more on localization + private const string Title = "System.Private.CoreLib ReadyToRun Intrinsics"; + private const string MessageFormat = "Intrinsics from class '{0}' used without the protection of an explicit if statement checking the correct IsSupported flag"; + private const string Description = "ReadyToRun Intrinsic Safety For System.Private.CoreLib."; + private const string Category = "IntrinsicsCorrectness"; + + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description); + + public const string DiagnosticIdConditionParsing = "IntrinsicsInSystemPrivateCoreLibConditionParsing"; + private const string MessageNonParseableConditionFormat = "Unable to parse condition to determine if intrinsics are correctly used"; + private static readonly DiagnosticDescriptor RuleCantParse = new DiagnosticDescriptor(DiagnosticIdConditionParsing, Title, MessageNonParseableConditionFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description); + + public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(Rule, RuleCantParse); } } + + private static INamespaceSymbol GetNamespace(IAssemblySymbol assembly, params string[] namespaceNames) + { + INamespaceSymbol outerNamespace = assembly.GlobalNamespace; + + string newFullNamespaceName = ""; + INamespaceSymbol? foundNamespace = null; + + foreach (var namespaceName in namespaceNames) + { + if (newFullNamespaceName == "") + newFullNamespaceName = namespaceName; + else + newFullNamespaceName = newFullNamespaceName + "." + namespaceName; + + foundNamespace = null; + + foreach (var innerNamespace in outerNamespace.GetNamespaceMembers()) + { + if (innerNamespace.Name == namespaceName) + { + foundNamespace = innerNamespace; + break; + } + } + + if (foundNamespace == null) + { + throw new Exception($"Not able to find {newFullNamespaceName} namespace"); + } + + outerNamespace = foundNamespace; + } + + return foundNamespace!; + } + + private static IEnumerable GetNestedTypes(INamedTypeSymbol type) + { + foreach (var typeSymbol in type.GetTypeMembers()) + { + yield return typeSymbol; + foreach (var nestedTypeSymbol in GetNestedTypes(typeSymbol)) + { + yield return nestedTypeSymbol; + } + } + } + + private static IEnumerable GetSubtypes(INamespaceSymbol namespaceSymbol) + { + foreach (var typeSymbol in namespaceSymbol.GetTypeMembers()) + { + yield return typeSymbol; + foreach (var nestedTypeSymbol in GetNestedTypes(typeSymbol)) + { + yield return nestedTypeSymbol; + } + } + + foreach (var namespaceMember in namespaceSymbol.GetNamespaceMembers()) + { + foreach (var typeSymbol in GetSubtypes(namespaceMember)) + { + yield return typeSymbol; + } + } + } + + private sealed class IntrinsicsAnalyzerOnLoadData + { + public IntrinsicsAnalyzerOnLoadData(List namedTypesToBeProtected, + INamedTypeSymbol? bypassReadyToRunAttribute, + INamedTypeSymbol? bypassReadyToRunForIntrinsicsHelperUse) + { + NamedTypesToBeProtected = namedTypesToBeProtected; + BypassReadyToRunAttribute = bypassReadyToRunAttribute; + BypassReadyToRunForIntrinsicsHelperUse = bypassReadyToRunForIntrinsicsHelperUse; + } + public readonly List NamedTypesToBeProtected; + public readonly INamedTypeSymbol? BypassReadyToRunAttribute; + public readonly INamedTypeSymbol? BypassReadyToRunForIntrinsicsHelperUse; + } + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterCompilationStartAction(context => + { + List namedTypesToBeProtected = new List(); + INamespaceSymbol systemRuntimeIntrinsicsNamespace = GetNamespace(context.Compilation.Assembly, "System", "Runtime", "Intrinsics"); + INamedTypeSymbol? bypassReadyToRunAttribute = context.Compilation.Assembly.GetTypeByMetadataName("System.Runtime.BypassReadyToRunAttribute"); + INamedTypeSymbol? bypassReadyToRunForIntrinsicsHelperUse = context.Compilation.Assembly.GetTypeByMetadataName("System.Runtime.BypassReadyToRunForIntrinsicsHelperUseAttribute"); + + IntrinsicsAnalyzerOnLoadData onLoadData = new IntrinsicsAnalyzerOnLoadData(namedTypesToBeProtected, bypassReadyToRunAttribute, bypassReadyToRunForIntrinsicsHelperUse); + + // Find all types in the System.Runtime.Intrinsics namespace that have an IsSupported property that are NOT + // directly in the System.Runtime.Intrinsics namespace + foreach (var architectureSpecificNamespace in systemRuntimeIntrinsicsNamespace.GetNamespaceMembers()) + { + foreach (var typeSymbol in GetSubtypes(architectureSpecificNamespace)) + { + foreach (var member in typeSymbol.GetMembers()) + { + if (member.Kind == SymbolKind.Property) + { + if (member.Name == "IsSupported") + { + namedTypesToBeProtected.Add(typeSymbol); + } + } + } + } + } + + context.RegisterSymbolStartAction(context => + { + var methodSymbol = (IMethodSymbol)context.Symbol; + + foreach (var attributeData in methodSymbol.GetAttributes()) + { + if (bypassReadyToRunAttribute != null) + { + if (attributeData.AttributeClass.Equals(bypassReadyToRunAttribute, SymbolEqualityComparer.Default)) + { + // This method isn't involved in ReadyToRun, and so doesn't need analysis + return; + } + } + } + + context.RegisterOperationAction(context => + { + AnalyzeOperation(context.Operation, methodSymbol, context, onLoadData); + }, + OperationKind.Invocation); + }, SymbolKind.Method); + }); + } + + private static ISymbol? GetOperationSymbol(IOperation operation) + => operation switch + { + IInvocationOperation iOperation => iOperation.TargetMethod, + IMemberReferenceOperation mOperation => mOperation.Member, + _ => null, + }; + + private static INamedTypeSymbol? GetIsSupportedTypeSymbol(SemanticModel model, MemberAccessExpressionSyntax memberAccessExpression) + { + if (memberAccessExpression.Name is IdentifierNameSyntax identifierName && identifierName.Identifier.Text == "IsSupported") + { + var symbolInfo = model.GetSymbolInfo(memberAccessExpression); + return symbolInfo.Symbol.ContainingSymbol as INamedTypeSymbol; + } + else + { + return null; + } + } + + private static INamedTypeSymbol[] GatherAndConditions(SemanticModel model, ExpressionSyntax expressionToDecompose) + { + if (expressionToDecompose is ParenthesizedExpressionSyntax parenthesizedExpression) + { + return GatherAndConditions(model, parenthesizedExpression.Expression); + } + + if (expressionToDecompose is MemberAccessExpressionSyntax memberAccessExpression) + { + var isSupportedType = GetIsSupportedTypeSymbol(model, memberAccessExpression); + if (isSupportedType == null) + { + return Array.Empty(); + } + else + return new INamedTypeSymbol[] { isSupportedType }; + } + else if (expressionToDecompose is BinaryExpressionSyntax binaryExpression) + { + if (binaryExpression.OperatorToken is SyntaxToken operatorToken && operatorToken.ValueText == "&&") + { + var decomposedLeft = GatherAndConditions(model, binaryExpression.Left); + var decomposedRight = GatherAndConditions(model, binaryExpression.Right); + int arrayLen = decomposedLeft.Length + decomposedRight.Length; + + if (arrayLen != 0) + { + var retVal = new INamedTypeSymbol[decomposedLeft.Length + decomposedRight.Length]; + Array.Copy(decomposedLeft, retVal, decomposedLeft.Length); + Array.Copy(decomposedRight, 0, retVal, decomposedLeft.Length, decomposedRight.Length); + return retVal; + } + else + { + return Array.Empty(); + } + } + } + + return Array.Empty(); + } + + private static INamedTypeSymbol[][] DecomposeConditionForIsSupportedGroups(SemanticModel model, ExpressionSyntax expressionToDecompose) + { + if (expressionToDecompose is ParenthesizedExpressionSyntax parenthesizedExpression) + { + return DecomposeConditionForIsSupportedGroups(model, parenthesizedExpression.Expression); + } + if (expressionToDecompose is MemberAccessExpressionSyntax memberAccessExpression) + { + var isSupportedType = GetIsSupportedTypeSymbol(model, memberAccessExpression); + if (isSupportedType == null) + { + return Array.Empty(); + } + else + { + return new INamedTypeSymbol[][] { new INamedTypeSymbol[] { isSupportedType } }; + } + } + else if (expressionToDecompose is BinaryExpressionSyntax binaryExpression) + { + var decomposedLeft = DecomposeConditionForIsSupportedGroups(model, binaryExpression.Left); + var decomposedRight = DecomposeConditionForIsSupportedGroups(model, binaryExpression.Right); + if (binaryExpression.OperatorToken is SyntaxToken operatorToken && operatorToken.ValueText == "&&") + { + return new INamedTypeSymbol[][] { GatherAndConditions(model, binaryExpression) }; + } + else if (binaryExpression.OperatorToken is SyntaxToken operatorToken2 && operatorToken2.ValueText == "||") + { + if (decomposedLeft.Length == 0 || decomposedRight.Length == 0) + { + return Array.Empty(); + } + var retVal = new INamedTypeSymbol[decomposedLeft.Length + decomposedRight.Length][]; + Array.Copy(decomposedLeft, retVal, decomposedLeft.Length); + Array.Copy(decomposedRight, 0, retVal, decomposedLeft.Length, decomposedRight.Length); + return retVal; + } + } + return Array.Empty(); + } + + private static bool ConditionAllowsSymbol(ISymbol symbolOfInvokeTarget, INamedTypeSymbol namedTypeThatIsSafeToUse) + { + HashSet examinedSymbols = new HashSet(SymbolEqualityComparer.Default); + Stack symbolsToExamine = new Stack(); + symbolsToExamine.Push(namedTypeThatIsSafeToUse); + + while (symbolsToExamine.Count > 0) + { + INamedTypeSymbol symbol = symbolsToExamine.Pop(); + if (symbolOfInvokeTarget.ContainingSymbol.Equals(symbol, SymbolEqualityComparer.Default)) + return true; + + examinedSymbols.Add(symbol); + if (symbol.ContainingType != null && !examinedSymbols.Contains(symbol.ContainingType)) + symbolsToExamine.Push(symbol.ContainingType); + if (symbol.BaseType != null && !examinedSymbols.Contains(symbol.BaseType)) + symbolsToExamine.Push(symbol.BaseType); + } + + return false; + } + + private static bool TypeSymbolAllowsTypeSymbol(INamedTypeSymbol namedTypeToCheckForSupport, INamedTypeSymbol namedTypeThatProvidesSupport) + { + HashSet examinedSymbols = new HashSet(SymbolEqualityComparer.Default); + Stack symbolsToExamine = new Stack(); + symbolsToExamine.Push(namedTypeThatProvidesSupport); + + while (symbolsToExamine.Count > 0) + { + INamedTypeSymbol symbol = symbolsToExamine.Pop(); + if (namedTypeToCheckForSupport.Equals(symbol, SymbolEqualityComparer.Default)) + return true; + + examinedSymbols.Add(symbol); + if (symbol.ContainingType != null && !examinedSymbols.Contains(symbol.ContainingType)) + symbolsToExamine.Push(symbol.ContainingType); + if (symbol.BaseType != null && !examinedSymbols.Contains(symbol.BaseType)) + symbolsToExamine.Push(symbol.BaseType); + } + + return false; + } + + private static void AnalyzeOperation(IOperation operation, IMethodSymbol methodSymbol, OperationAnalysisContext context, IntrinsicsAnalyzerOnLoadData onLoadData) + { + var symbol = GetOperationSymbol(operation); + + if (symbol == null || symbol is ITypeSymbol type && type.SpecialType != SpecialType.None) + { + return; + } + + if (methodSymbol.ContainingType.Equals(symbol.ContainingSymbol, SymbolEqualityComparer.Default)) + { + return; // Intrinsic functions on their containing type can call themselves + } + + bool methodNeedsProtectionWithIsSupported = false; + foreach (var search in onLoadData.NamedTypesToBeProtected) + { + if (search.Equals(symbol.ContainingSymbol, SymbolEqualityComparer.Default)) + { + methodNeedsProtectionWithIsSupported = true; + } + } + + if (!methodNeedsProtectionWithIsSupported) + return; + + if (symbol is IPropertySymbol propertySymbol) + { + if (propertySymbol.Name == "IsSupported") + { + return; + } + } + + ISymbol? symbolThatMightHaveIntrinsicsHelperAttribute = methodSymbol; + IOperation operationSearch = operation; + while (operationSearch != null) + { + if (operationSearch.Kind == OperationKind.AnonymousFunction) + { + symbolThatMightHaveIntrinsicsHelperAttribute = null; + break; + } + if (operationSearch.Kind == OperationKind.LocalFunction) + { + // Assign symbolThatMightHaveIntrinsicsHelperAttribute to the symbol for the LocalFunction + ILocalFunctionOperation localFunctionOperation = (ILocalFunctionOperation)operationSearch; + symbolThatMightHaveIntrinsicsHelperAttribute = localFunctionOperation.Symbol; + break; + } + + operationSearch = operationSearch.Parent; + } + + var bypassReadyToRunForIntrinsicsHelperUse = onLoadData.BypassReadyToRunForIntrinsicsHelperUse; + if ((bypassReadyToRunForIntrinsicsHelperUse != null) && symbolThatMightHaveIntrinsicsHelperAttribute != null) + { + foreach (var attributeData in symbolThatMightHaveIntrinsicsHelperAttribute.GetAttributes()) + { + if (attributeData.AttributeClass.Equals(bypassReadyToRunForIntrinsicsHelperUse, SymbolEqualityComparer.Default)) + { + if (attributeData.ConstructorArguments[0].Value is INamedTypeSymbol attributeTypeSymbol && ConditionAllowsSymbol(symbol, attributeTypeSymbol)) + { + // This attribute indicates that this method will only be compiled into a ReadyToRun image if the behavior + // of the associated IsSupported method is defined to a constant value during ReadyToRun compilation that cannot change at runtime + return; + } + } + } + } + + var ancestorNodes = operation.Syntax.AncestorsAndSelf(true); + SyntaxNode? previousNode = null; + HashSet notTypes = new HashSet(SymbolEqualityComparer.Default); + + foreach (var ancestorNode in ancestorNodes) + { + if (previousNode != null) + { + if (ancestorNode is LocalFunctionStatementSyntax) + { + // Local functions are not the same ECMA 335 function as the outer function, so don't continue searching for an if statement. + break; + } + if (ancestorNode is LambdaExpressionSyntax) + { + // Lambda functions are not the same ECMA 335 function as the outer function, so don't continue searching for an if statement. + break; + } + if (ancestorNode is IfStatementSyntax ifStatement) + { + if (HandleConditionalCase(ifStatement.Condition, ifStatement.Statement, ifStatement.Else)) + return; + } + if (ancestorNode is ConditionalExpressionSyntax conditionalExpression) + { + if (HandleConditionalCase(conditionalExpression.Condition, conditionalExpression.WhenTrue, conditionalExpression.WhenFalse)) + return; + } + + // Returns true to indicate the wrapping method should return + bool HandleConditionalCase(ExpressionSyntax condition, SyntaxNode? syntaxOnPositiveCondition, SyntaxNode? syntaxOnNegativeCondition) + { + if (previousNode == syntaxOnPositiveCondition) + { + var decomposedCondition = DecomposeConditionForIsSupportedGroups(operation.SemanticModel, condition); + if (decomposedCondition.Length == 1) + { + foreach (var symbolFromCondition in decomposedCondition[0]) + { + if (ConditionAllowsSymbol(symbol, symbolFromCondition)) + { + // There is a good IsSupported check with a positive check for the IsSupported call involved. Do not report. + return true; + } + } + } + else if (decomposedCondition.Length > 1) + { + // Ensure every symbol found in the condition is only in 1 OR clause + HashSet foundSymbols = new HashSet(SymbolEqualityComparer.Default); + foreach (var andClause in decomposedCondition) + { + foreach (var symbolInOrClause in andClause) + { + if (!foundSymbols.Add(symbolInOrClause)) + { + context.ReportDiagnostic(Diagnostic.Create(RuleCantParse, operation.Syntax.GetLocation())); + return true; + } + } + } + + // Check to see if all of the OR conditions other than 1 have been eliminated via if statements excluding those symbols + int indexNotExcluded = -1; + for (int andClauseIndex = 0; andClauseIndex < decomposedCondition.Length; andClauseIndex++) + { + bool foundMatchInAndClause = false; + foreach (var symbolInAndClause in decomposedCondition[andClauseIndex]) + { + foreach (var notType in notTypes) + { + if (TypeSymbolAllowsTypeSymbol(notType, symbolInAndClause)) + { + foundMatchInAndClause = true; + break; + } + } + if (foundMatchInAndClause) + break; + } + + if (!foundMatchInAndClause) + { + if (indexNotExcluded == -1) + { + indexNotExcluded = andClauseIndex; + } + else + { + // Multiple And clause groups not excluded. We didn't find a unique one. + indexNotExcluded = -1; + break; + } + } + } + + if (indexNotExcluded != -1) + { + var andClause = decomposedCondition[indexNotExcluded]; + foreach (var symbolFromCondition in andClause) + { + if (ConditionAllowsSymbol(symbol, symbolFromCondition)) + { + // There is a good IsSupported check with a positive check for the IsSupported call involved. Do not report. + return true; + } + } + } + } + } + else if (previousNode == syntaxOnNegativeCondition) + { + var decomposedCondition = DecomposeConditionForIsSupportedGroups(operation.SemanticModel, condition); + if (decomposedCondition.Length == 1) + { + foreach (var symbolFromCondition in decomposedCondition[0]) + { + notTypes.Add(symbolFromCondition); + } + } + } + + return false; + } + } + previousNode = ancestorNode; + } + + context.ReportDiagnostic(Diagnostic.Create(Rule, operation.Syntax.GetLocation(), symbol.ContainingSymbol.ToDisplayString())); + } + } +} diff --git a/src/libraries/System.Private.CoreLib/gen/System.Private.CoreLib.Generators.csproj b/src/libraries/System.Private.CoreLib/gen/System.Private.CoreLib.Generators.csproj index 34e09d27657253..fa1603a0091095 100644 --- a/src/libraries/System.Private.CoreLib/gen/System.Private.CoreLib.Generators.csproj +++ b/src/libraries/System.Private.CoreLib/gen/System.Private.CoreLib.Generators.csproj @@ -8,6 +8,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 0759d0370079f0..e362f90f7804a8 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -750,6 +750,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs index 2899c7d1021bfc..550e3c3397eb1f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs @@ -353,6 +353,7 @@ public static unsafe OperationStatus DecodeFromUtf8InPlace(Span buffer, ou [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static unsafe void Avx2Decode(ref byte* srcBytes, ref byte* destBytes, byte* srcEnd, int sourceLength, int destLength, byte* srcStart, byte* destStart) { // If we have AVX2 support, pick off 32 bytes at a time for as long as we can, @@ -493,6 +494,8 @@ private static Vector128 SimdShuffle(Vector128 left, Vector128 } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] private static unsafe void Vector128Decode(ref byte* srcBytes, ref byte* destBytes, byte* srcEnd, int sourceLength, int destLength, byte* srcStart, byte* destStart) { Debug.Assert((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian); diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Encoder.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Encoder.cs index 83fe75aef335d1..da3793be76d9ca 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Encoder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Encoder.cs @@ -228,6 +228,7 @@ public static unsafe OperationStatus EncodeToUtf8InPlace(Span buffer, int [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static unsafe void Avx2Encode(ref byte* srcBytes, ref byte* destBytes, byte* srcEnd, int sourceLength, int destLength, byte* srcStart, byte* destStart) { // If we have AVX2 support, pick off 24 bytes at a time for as long as we can. @@ -398,6 +399,8 @@ private static unsafe void Avx2Encode(ref byte* srcBytes, ref byte* destBytes, b } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] private static unsafe void Vector128Encode(ref byte* srcBytes, ref byte* destBytes, byte* srcEnd, int sourceLength, int destLength, byte* srcStart, byte* destStart) { // If we have SSSE3 support, pick off 12 bytes at a time for as long as we can. diff --git a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAnyAsciiSearcher.cs b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAnyAsciiSearcher.cs index ce0d0bd5be94bd..f31a41e9fd7ba6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAnyAsciiSearcher.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAnyAsciiSearcher.cs @@ -809,6 +809,9 @@ internal static int LastIndexOfAnyVectorized(ref byte searchSpace, int } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] private static Vector128 IndexOfAnyLookup(Vector128 source0, Vector128 source1, Vector128 bitmapLookup) where TNegator : struct, INegator where TOptimizations : struct, IOptimizations @@ -872,6 +875,7 @@ private static Vector128 IndexOfAnyLookupCore(Vector128 source, Vect [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static Vector256 IndexOfAnyLookup(Vector256 source0, Vector256 source1, Vector256 bitmapLookup) where TNegator : struct, INegator where TOptimizations : struct, IOptimizations @@ -893,6 +897,7 @@ private static Vector256 IndexOfAnyLookup(Vector [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static Vector256 IndexOfAnyLookupCore(Vector256 source, Vector256 bitmapLookup) { // See comments in IndexOfAnyLookupCore(Vector128) above for more details. @@ -927,6 +932,7 @@ private static Vector128 IndexOfAnyLookup(Vector128 source [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static Vector256 IndexOfAnyLookup(Vector256 source, Vector256 bitmapLookup0, Vector256 bitmapLookup1) where TNegator : struct, INegator { @@ -1074,6 +1080,7 @@ private static unsafe int ComputeLastIndexOverlapped(ref T searchSp [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static Vector256 FixUpPackedVector256Result(Vector256 result) { Debug.Assert(Avx2.IsSupported); diff --git a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/ProbabilisticMap.cs b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/ProbabilisticMap.cs index efd348aa99ae11..b9a14754c7690d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/ProbabilisticMap.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/ProbabilisticMap.cs @@ -107,6 +107,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(values)), [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static Vector256 ContainsMask32CharsAvx2(Vector256 charMapLower, Vector256 charMapUpper, ref char searchSpace) { Vector256 source0 = Vector256.LoadUnsafe(ref searchSpace); @@ -128,6 +129,7 @@ private static Vector256 ContainsMask32CharsAvx2(Vector256 charMapLo [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static Vector256 IsCharBitSetAvx2(Vector256 charMapLower, Vector256 charMapUpper, Vector256 values) { // X86 doesn't have a logical right shift intrinsic for bytes: https://github.com/dotnet/runtime/issues/82564 @@ -145,6 +147,8 @@ private static Vector256 IsCharBitSetAvx2(Vector256 charMapLower, Ve } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] private static Vector128 ContainsMask16Chars(Vector128 charMapLower, Vector128 charMapUpper, ref char searchSpace) { Vector128 source0 = Vector128.LoadUnsafe(ref searchSpace); diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix4x4.Impl.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix4x4.Impl.cs index f0a3b4f2128a57..9d1244bef475f7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix4x4.Impl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix4x4.Impl.cs @@ -1055,6 +1055,7 @@ public static bool Invert(in Impl matrix, out Impl result) return SoftwareFallback(in matrix, out result); + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse))] static bool SseImpl(in Impl matrix, out Impl result) { if (!Sse.IsSupported) diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/VectorMath.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/VectorMath.cs index c8b4be93ac6ea4..7c00f683c5d0a8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/VectorMath.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/VectorMath.cs @@ -113,7 +113,7 @@ public static bool NotEqual(Vector128 vector1, Vector128 vector2) // This implementation is based on the DirectX Math Library XMVector4NotEqual method // https://github.com/microsoft/DirectXMath/blob/master/Inc/DirectXMathVector.inl - if (AdvSimd.IsSupported) + if (AdvSimd.Arm64.IsSupported) { Vector128 vResult = AdvSimd.CompareEqual(vector1, vector2).AsUInt32(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/BypassReadyToRunForIntrinsicsHelperUse.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/BypassReadyToRunForIntrinsicsHelperUse.cs new file mode 100644 index 00000000000000..20db6e12a809cf --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/BypassReadyToRunForIntrinsicsHelperUse.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime +{ + // Use this attribute to indicate that a function should only be compiled into a Ready2Run + // binary if the associated type will always have a well defined value for its IsSupported property + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, AllowMultiple = true, Inherited = false)] + internal sealed class BypassReadyToRunForIntrinsicsHelperUseAttribute : Attribute + { + public BypassReadyToRunForIntrinsicsHelperUseAttribute(Type intrinsicsTypeUsedInHelperFunction) + { + IntrinsicsTypeUsedInHelperFunction = intrinsicsTypeUsedInHelperFunction; + } + + public Type IntrinsicsTypeUsedInHelperFunction { get; } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs index d1c7d5ee8a726c..ae6476746c9c75 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs @@ -3218,6 +3218,8 @@ internal static void SetUpperUnsafe(in this Vector128 vector, Vector64 } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] internal static Vector128 UnpackLow(Vector128 left, Vector128 right) { if (Sse2.IsSupported) @@ -3232,6 +3234,8 @@ internal static Vector128 UnpackLow(Vector128 left, Vector128 } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] internal static Vector128 UnpackHigh(Vector128 left, Vector128 right) { if (Sse2.IsSupported) @@ -3248,6 +3252,8 @@ internal static Vector128 UnpackHigh(Vector128 left, Vector128 // TODO: Make generic versions of these public, see https://github.com/dotnet/runtime/issues/82559 [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] internal static Vector128 AddSaturate(Vector128 left, Vector128 right) { if (Sse2.IsSupported) @@ -3262,6 +3268,8 @@ internal static Vector128 AddSaturate(Vector128 left, Vector128 SubtractSaturate(Vector128 left, Vector128 right) { if (Sse2.IsSupported) diff --git a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs index c48622ca293550..4fc5462988de0f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs @@ -787,6 +787,7 @@ private static int IndexOfAnyInRange(ref short searchSpace, short lowI [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static Vector256 PackSources(Vector256 source0, Vector256 source1) { Debug.Assert(Avx2.IsSupported); @@ -798,6 +799,7 @@ private static Vector256 PackSources(Vector256 source0, Vector256 PackSources(Vector128 source0, Vector128 source1) { Debug.Assert(Sse2.IsSupported); @@ -866,6 +868,7 @@ private static int ComputeFirstIndexOverlapped(ref short searchSpace, ref short [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static Vector256 FixUpPackedVector256Result(Vector256 result) { Debug.Assert(Avx2.IsSupported); diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Utility.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Utility.cs index 4acf6e82baa6ac..0965d9a90609a7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Utility.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Utility.cs @@ -53,6 +53,7 @@ private static bool AllCharsInUInt64AreAscii(ulong value) } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] private static int GetIndexOfFirstNonAsciiByteInLane_AdvSimd(Vector128 value, Vector128 bitmask) { if (!AdvSimd.Arm64.IsSupported || !BitConverter.IsLittleEndian) @@ -1478,6 +1479,7 @@ private static bool AllCharsInVectorAreAscii(Vector128 vector) } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx))] private static bool AllCharsInVectorAreAscii(Vector256 vector) where T : unmanaged { diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Latin1Utility.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Latin1Utility.cs index e579f6fa9d56b1..2450fa432c1a88 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Latin1Utility.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Latin1Utility.cs @@ -163,6 +163,7 @@ private static unsafe nuint GetIndexOfFirstNonLatin1Char_Default(char* pBuffer, goto Finish; } + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] private static unsafe nuint GetIndexOfFirstNonLatin1Char_Sse2(char* pBuffer, nuint bufferLength /* in chars */) { // This method contains logic optimized for both SSE2 and SSE41. Much of the logic in this method @@ -750,6 +751,7 @@ public static unsafe nuint NarrowUtf16ToLatin1(char* pUtf16Buffer, byte* pLatin1 goto Finish; } + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] private static unsafe nuint NarrowUtf16ToLatin1_Sse2(char* pUtf16Buffer, byte* pLatin1Buffer, nuint elementCount) { // This method contains logic optimized for both SSE2 and SSE41. Much of the logic in this method @@ -940,6 +942,7 @@ public static unsafe void WidenLatin1ToUtf16(byte* pLatin1Buffer, char* pUtf16Bu } } + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] private static unsafe void WidenLatin1ToUtf16_Sse2(byte* pLatin1Buffer, char* pUtf16Buffer, nuint elementCount) { // JIT turns the below into constants diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.Validation.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.Validation.cs index 199d494f6e174a..6bf314dba8ca97 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.Validation.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.Validation.cs @@ -489,6 +489,7 @@ internal static unsafe partial class Utf16Utility } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] private static uint GetNonAsciiBytes(Vector128 value, Vector128 bitMask128) { Debug.Assert(AdvSimd.Arm64.IsSupported); diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.Transcoding.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.Transcoding.cs index d0b31f57072067..76c4bed4d9a769 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.Transcoding.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.Transcoding.cs @@ -953,7 +953,7 @@ public static OperationStatus TranscodeToUtf8(char* pInputBuffer, int inputLengt utf16Data = Unsafe.ReadUnaligned>(pInputBuffer); - if (AdvSimd.IsSupported) + if (AdvSimd.Arm64.IsSupported) { Vector128 isUtf16DataNonAscii = AdvSimd.CompareTest(utf16Data, nonAsciiUtf16DataMask); bool hasNonAsciiDataInVector = AdvSimd.Arm64.MinPairwise(isUtf16DataNonAscii, isUtf16DataNonAscii).AsUInt64().ToScalar() != 0; diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.Validation.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.Validation.cs index 91bd846b41e091..6cfd21a1e25a96 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.Validation.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.Validation.cs @@ -740,6 +740,7 @@ internal static unsafe partial class Utf8Utility } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] private static ulong GetNonAsciiBytes(Vector128 value, Vector128 bitMask128) { if (!AdvSimd.Arm64.IsSupported || !BitConverter.IsLittleEndian) From b246bd0be9989f45480f5678d5cf0649b5dab806 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 26 Apr 2023 15:52:51 -0700 Subject: [PATCH 02/19] Remove BypassReadyToRunAttribute as it should no longer be needed for intrinsics usages --- .../src/System.Private.CoreLib.Shared.projitems | 1 - .../src/System/Buffers/Text/Base64Decoder.cs | 1 - .../src/System/Buffers/Text/Base64Encoder.cs | 1 - .../System/IndexOfAnyValues/IndexOfAnyAsciiSearcher.cs | 8 -------- .../src/System/IndexOfAnyValues/ProbabilisticMap.cs | 2 -- .../src/System/Runtime/BypassReadyToRunAttribute.cs | 10 ---------- .../src/System/SpanHelpers.Packed.cs | 4 ---- 7 files changed, 27 deletions(-) delete mode 100644 src/libraries/System.Private.CoreLib/src/System/Runtime/BypassReadyToRunAttribute.cs diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index e362f90f7804a8..4cc8abb81e3be0 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -749,7 +749,6 @@ - diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs index 550e3c3397eb1f..ab5e2af084066b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs @@ -351,7 +351,6 @@ public static unsafe OperationStatus DecodeFromUtf8InPlace(Span buffer, ou } } - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static unsafe void Avx2Decode(ref byte* srcBytes, ref byte* destBytes, byte* srcEnd, int sourceLength, int destLength, byte* srcStart, byte* destStart) diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Encoder.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Encoder.cs index da3793be76d9ca..1bb7e030964488 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Encoder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Encoder.cs @@ -226,7 +226,6 @@ public static unsafe OperationStatus EncodeToUtf8InPlace(Span buffer, int } } - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static unsafe void Avx2Encode(ref byte* srcBytes, ref byte* destBytes, byte* srcEnd, int sourceLength, int destLength, byte* srcStart, byte* destStart) diff --git a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAnyAsciiSearcher.cs b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAnyAsciiSearcher.cs index f31a41e9fd7ba6..93d6878ce1fe1b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAnyAsciiSearcher.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAnyAsciiSearcher.cs @@ -873,7 +873,6 @@ private static Vector128 IndexOfAnyLookupCore(Vector128 source, Vect return result; } - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static Vector256 IndexOfAnyLookup(Vector256 source0, Vector256 source1, Vector256 bitmapLookup) @@ -895,7 +894,6 @@ private static Vector256 IndexOfAnyLookup(Vector return TNegator.NegateIfNeeded(result); } - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static Vector256 IndexOfAnyLookupCore(Vector256 source, Vector256 bitmapLookup) @@ -930,7 +928,6 @@ private static Vector128 IndexOfAnyLookup(Vector128 source return TNegator.NegateIfNeeded(result); } - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static Vector256 IndexOfAnyLookup(Vector256 source, Vector256 bitmapLookup0, Vector256 bitmapLookup1) @@ -1002,7 +999,6 @@ private static unsafe int ComputeLastIndexOverlapped(ref T searchSp return offsetInVector - Vector128.Count + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref secondVector) / (nuint)sizeof(T)); } - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe int ComputeFirstIndex(ref T searchSpace, ref T current, Vector256 result) where TNegator : struct, INegator @@ -1018,7 +1014,6 @@ private static unsafe int ComputeFirstIndex(ref T searchSpace, ref return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current) / (nuint)sizeof(T)); } - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe int ComputeFirstIndexOverlapped(ref T searchSpace, ref T current0, ref T current1, Vector256 result) where TNegator : struct, INegator @@ -1040,7 +1035,6 @@ private static unsafe int ComputeFirstIndexOverlapped(ref T searchS return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current0) / (nuint)sizeof(T)); } - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe int ComputeLastIndex(ref T searchSpace, ref T current, Vector256 result) where TNegator : struct, INegator @@ -1056,7 +1050,6 @@ private static unsafe int ComputeLastIndex(ref T searchSpace, ref T return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current) / (nuint)sizeof(T)); } - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe int ComputeLastIndexOverlapped(ref T searchSpace, ref T secondVector, Vector256 result) where TNegator : struct, INegator @@ -1078,7 +1071,6 @@ private static unsafe int ComputeLastIndexOverlapped(ref T searchSp return offsetInVector - Vector256.Count + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref secondVector) / (nuint)sizeof(T)); } - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static Vector256 FixUpPackedVector256Result(Vector256 result) diff --git a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/ProbabilisticMap.cs b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/ProbabilisticMap.cs index b9a14754c7690d..340304b3224d24 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/ProbabilisticMap.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/ProbabilisticMap.cs @@ -105,7 +105,6 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(values)), (short)ch, values.Length); - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static Vector256 ContainsMask32CharsAvx2(Vector256 charMapLower, Vector256 charMapUpper, ref char searchSpace) @@ -127,7 +126,6 @@ private static Vector256 ContainsMask32CharsAvx2(Vector256 charMapLo return resultLower & resultUpper; } - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static Vector256 IsCharBitSetAvx2(Vector256 charMapLower, Vector256 charMapUpper, Vector256 values) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/BypassReadyToRunAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/BypassReadyToRunAttribute.cs deleted file mode 100644 index d4def4f9eadb8b..00000000000000 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/BypassReadyToRunAttribute.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Runtime -{ - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false)] - internal sealed class BypassReadyToRunAttribute : Attribute - { - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs index 4fc5462988de0f..fe559da2db1d18 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs @@ -785,7 +785,6 @@ private static int IndexOfAnyInRange(ref short searchSpace, short lowI return -1; } - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static Vector256 PackSources(Vector256 source0, Vector256 source1) @@ -828,7 +827,6 @@ private static int ComputeFirstIndex(ref short searchSpace, ref short current, V return index + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current) / sizeof(short)); } - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int ComputeFirstIndex(ref short searchSpace, ref short current, Vector256 equals) { @@ -851,7 +849,6 @@ private static int ComputeFirstIndexOverlapped(ref short searchSpace, ref short return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current0) / sizeof(short)); } - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int ComputeFirstIndexOverlapped(ref short searchSpace, ref short current0, ref short current1, Vector256 equals) { @@ -866,7 +863,6 @@ private static int ComputeFirstIndexOverlapped(ref short searchSpace, ref short return offsetInVector + (int)((nuint)Unsafe.ByteOffset(ref searchSpace, ref current0) / sizeof(short)); } - [BypassReadyToRun] [MethodImpl(MethodImplOptions.AggressiveInlining)] [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static Vector256 FixUpPackedVector256Result(Vector256 result) From 35105fb98480a506228b608ebbce6a056ae46ab6 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 27 Apr 2023 12:05:59 -0700 Subject: [PATCH 03/19] Document new intrinsic handling behavior and enhance crossgen2 to respect the new attribute --- .../coreclr/botr/vectors-and-intrinsics.md | 165 +++----- .../JitInterface/CorInfoInstructionSet.cs | 365 ++++++++++++++++++ .../ThunkGenerator/InstructionSetDesc.txt | 6 +- .../ThunkGenerator/InstructionSetGenerator.cs | 122 +++++- .../ReadyToRunCompilationModuleGroupBase.cs | 9 + .../Compiler/ReadyToRunLibraryRootProvider.cs | 2 +- .../ReadyToRunProfilingRootProvider.cs | 2 +- .../ReadyToRunVisibilityRootProvider.cs | 2 +- .../Compiler/ReadyToRunXmlRootProvider.cs | 2 +- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 84 +++- src/coreclr/tools/aot/crossgen2/Program.cs | 1 + .../Common/src/System/HexConverter.cs | 1 + .../System/Runtime/Intrinsics/Vector128.cs | 2 + 13 files changed, 632 insertions(+), 131 deletions(-) diff --git a/docs/design/coreclr/botr/vectors-and-intrinsics.md b/docs/design/coreclr/botr/vectors-and-intrinsics.md index f507b55bd622f1..dd6b9918da4636 100644 --- a/docs/design/coreclr/botr/vectors-and-intrinsics.md +++ b/docs/design/coreclr/botr/vectors-and-intrinsics.md @@ -38,123 +38,6 @@ For AOT compilation, the situation is far more complex. This is due to the follo 2. If AOT code is generated, it should be used unless there is an overriding reason to avoid using it. 3. It must be exceedingly difficult to misuse the AOT compilation tool to violate principle 1. -There are 2 different implementations of AOT compilation under development at this time. The crossgen1 model (which is currently supported on all platforms and architectures), and the crossgen2 model, which is under active development. Any developer wishing to use hardware intrinsics in the runtime or libraries should be aware of the restrictions imposed by the crossgen1 model. Crossgen2, which we expect will replace crossgen1 at some point in the future, has strictly fewer restrictions. - -## Crossgen1 model of hardware intrinsic usage - -###Code written in System.Private.CoreLib.dll -#### Crossgen implementation rules -- Any code which uses `Vector` will not be compiled AOT. (See code which throws a TypeLoadException using `IDS_EE_SIMD_NGEN_DISALLOWED`) -- Code which uses Sse and Sse2 platform hardware intrinsics is always generated as it would be at jit time. -- Code which uses Sse3, Ssse3, Sse41, Sse42, Popcnt, Pclmulqdq, and Lzcnt instruction sets will be generated, but the associated IsSupported check will be a runtime check. See `FilterNamedIntrinsicMethodAttribs` for details on how this is done. -- Code which uses other instruction sets will be generated as if the processor does not support that instruction set. (For instance, a usage of Avx2.IsSupported in CoreLib will generate native code where it unconditionally returns false, and then if and when tiered compilation occurs, the function may be rejitted and have code where the property returns true.) -- Non-platform intrinsics which require more hardware support than the minimum supported hardware capability will not take advantage of that capability. In particular the code generated for `Vector2/3/4.Dot`, and `Math.Round`, and `MathF.Round`. See `FilterNamedIntrinsicMethodAttribs` for details. MethodImplOptions.AggressiveOptimization may be used to disable precompilation compilation of this sub-par code. - -#### Characteristics which result from rules -The rules here provide the following characteristics. -- Some platform specific hardware intrinsics can be used in CoreLib without encountering a startup time penalty -- Some uses of platform specific hardware intrinsics will force the compiler to be unable to AOT compile the code. However, if care is taken to only use intrinsics from the Sse, Sse2, Sse3, Ssse3, Sse41, Sse42, Popcnt, Pclmulqdq, or Lzcnt instruction sets, then the code may be AOT compiled. Preventing AOT compilation may cause a startup time penalty for important scenarios. -- Use of `Vector` causes runtime jit and startup time concerns because it is never precompiled. Current analysis indicates this is acceptable, but it is a perennial concern for applications with tight startup time requirements. -- AOT generated code which could take advantage of more advanced hardware support experiences a performance penalty until rejitted. (If a customer chooses to disable tiered compilation, then customer code may always run slowly). - -#### Code review rules for code written in System.Private.CoreLib.dll -- Any use of a platform intrinsic in the codebase MUST be wrapped with a call to the associated IsSupported property. This wrapping MUST be done within the same function that uses the hardware intrinsic, and MUST NOT be in a wrapper function unless it is one of the intrinsics that are enabled by default for crossgen compilation of System.Private.CoreLib (See list above in the implementation rules section). -- Within a single function that uses platform intrinsics, it must behave identically regardless of whether IsSupported returns true or not. This rule is required as code inside of an IsSupported check that calls a helper function cannot assume that the helper function will itself see its use of the same IsSupported check return true. This is due to the impact of tiered compilation on code execution within the process. -- Excessive use of intrinsics may cause startup performance problems due to additional jitting, or may not achieve desired performance characteristics due to suboptimal codegen. - -ACCEPTABLE Code -```csharp -using System.Runtime.Intrinsics.X86; - -public class BitOperations -{ - public static int PopCount(uint value) - { - if (Avx2.IsSupported) - { - Some series of Avx2 instructions that performs the popcount operation. - } - else - return FallbackPath(input); - } - - private static int FallbackPath(uint) - { - const uint c1 = 0x_55555555u; - const uint c2 = 0x_33333333u; - const uint c3 = 0x_0F0F0F0Fu; - const uint c4 = 0x_01010101u; - - value -= (value >> 1) & c1; - value = (value & c2) + ((value >> 2) & c2); - value = (((value + (value >> 4)) & c3) * c4) >> 24; - - return (int)value; - } -} -``` - -UNACCEPTABLE code -```csharp -using System.Runtime.Intrinsics.X86; - -public class BitOperations -{ - public static int PopCount(uint value) - { - if (Avx2.IsSupported) - return UseAvx2(value); - else - return FallbackPath(input); - } - - private static int FallbackPath(uint) - { - const uint c1 = 0x_55555555u; - const uint c2 = 0x_33333333u; - const uint c3 = 0x_0F0F0F0Fu; - const uint c4 = 0x_01010101u; - - value -= (value >> 1) & c1; - value = (value & c2) + ((value >> 2) & c2); - value = (((value + (value >> 4)) & c3) * c4) >> 24; - - return (int)value; - } - - private static int UseAvx2(uint value) - { - // THIS IS A BUG!!!!! - Some series of Avx2 instructions that performs the popcount operation. - The bug here is triggered by the presence of tiered compilation and R2R. The R2R version - of this method may be compiled as if the Avx2 feature is not available, and is not reliably rejitted - at the same time as the PopCount function. - - As a special note, on the x86 and x64 platforms, this generally unsafe pattern may be used - with the Sse, Sse2, Sse3, Sssse3, Ssse41 and Sse42 instruction sets as those instruction sets - are treated specially by both crossgen1 and crossgen2 when compiling System.Private.CoreLib.dll. - } -} -``` - -### Code written in other assemblies (both first and third party) - -#### Crossgen implementation rules -- Any code which uses an intrinsic from the `System.Runtime.Intrinsics.Arm` or `System.Runtime.Intrinsics.X86` namespace will not be compiled AOT. (See code which throws a TypeLoadException using `IDS_EE_HWINTRINSIC_NGEN_DISALLOWED`) -- Any code which uses `Vector` will not be compiled AOT. (See code which throws a TypeLoadException using `IDS_EE_SIMD_NGEN_DISALLOWED`) -- Any code which uses `Vector64`, `Vector128`, `Vector256`, or `Vector512` will not be compiled AOT. (See code which throws a TypeLoadException using `IDS_EE_HWINTRINSIC_NGEN_DISALLOWED`) -- Non-platform intrinsics which require more hardware support than the minimum supported hardware capability will not take advantage of that capability. In particular the code generated for Vector2/3/4 is sub-optimal. MethodImplOptions.AggressiveOptimization may be used to disable compilation of this sub-par code. - -#### Characteristics which result from rules -The rules here provide the following characteristics. -- Use of platform specific hardware intrinsics causes runtime jit and startup time concerns. -- Use of `Vector` causes runtime jit and startup time concerns -- AOT generated code which could take advantage of more advanced hardware support experiences a performance penalty until rejitted. (If a customer chooses to disable tiered compilation, then customer code may always run slowly). - -#### Code review rules for use of platform intrinsics -- Any use of a platform intrinsic in the codebase SHOULD be wrapped with a call to the associated IsSupported property. This wrapping may be done within the same function that uses the hardware intrinsic, but this is not required as long as the programmer can control all entrypoints to a function that uses the hardware intrinsic. -- If an application developer is highly concerned about startup performance, developers should avoid use of all platform specific hardware intrinsics on startup paths. - ## Crossgen2 model of hardware intrinsic usage There are 2 sets of instruction sets known to the compiler. - The baseline instruction set which defaults to (Sse, Sse2), but may be adjusted via compiler option. @@ -178,9 +61,55 @@ Code will be compiled using the optimistic instruction set to drive compilation, - If an application developer is highly concerned about startup performance, developers should avoid use intrinsics beyond Sse42, or should use Crossgen with an updated baseline instruction set support. ### Crossgen2 adjustment to rules for System.Private.CoreLib.dll -Since System.Private.CoreLib.dll is known to be code reviewed with the code review rules as written above for crossgen1 with System.Private.CoreLib.dll, it is possible to relax rule "Code which attempts to use instruction sets outside of the optimistic set will generate code that will not be used on machines with support for the instruction set." What this will do is allow the generation of non-optimal code for these situations, but through the magic of code review, the generated logic will still work correctly. +Since System.Private.CoreLib.dll is known to be code reviewed with the code review rules as written above for crossgen1 with System.Private.CoreLib.dll, it is possible to relax rule "Code which attempts to use instruction sets outside of the optimistic set will generate code that will not be used on machines with support for the instruction set." What this will do is allow the generation of non-optimal code for these situations, but through the magic of code review and analyzers, the generated logic will still work correctly. + +#### Code review rules for code written in System.Private.CoreLib.dll +- Any use of a platform intrinsic in the codebase MUST be wrapped with a call to an associated IsSupported property. This wrapping MUST be done within the same function that uses the hardware intrinsic, OR the function which uses the platform intrinsic must have the `BypassReadyToRunForIntrinsicsHelperUse` attribute used to indicate that this function will unconditionally call platform intrinsics of from some type. +- Within a single function that uses platform intrinsics, unless marked with the `BypassReadyToRunForIntrinsicsHelperUse` attribute it must behave identically regardless of whether IsSupported returns true or not. This allows the R2R compiler to compile with a lower set of intrinsics support, and yet expect that the behavior of the function will remain unchanged in the presence of tiered compilation. +- Excessive use of intrinsics may cause startup performance problems due to additional jitting, or may not achieve desired performance characteristics due to suboptimal codegen. To fix this, we may, in the future, change the compilation rules to compile the methods marked with`BypassReadyToRunForIntrinsicsHelperUse` with the appropriate platform intrinsics enabled. + +Correct use of the `IsSupported` properties and `BypassReadyToRunForIntrinsicsHelperUse` attribute is checked by an analyzer during build of `System.Private.CoreLib`. This analyzer requires that all usage of `IsSupported` properties conform to a few specific patterns. These patterns are supported via either if statements or the ternary operator. + +The supported conditional checks are + +1. Simple if statement checking IsSupported flag surrounding usage +``` +if (PlatformIntrinsicType.IsSupported) +{ + PlatformIntrinsicType.IntrinsicMethod(); +} +``` + +2. If statement check checking a platform intrinsic type which implies +that the intrinsic used is supported. + +``` +if (Avx2.X64.IsSupported) +{ + Avx2.IntrinsicMethod(); +} +``` + +3. Nested if statement where there is an outer condition which is an +OR'd together series of IsSupported checks for mutually exclusive +conditions and where the inner check is an else clause where some checks +are excluded from applying. +``` +if (Avx2.IsSupported || ArmBase.IsSupported) +{ + if (Avx2.IsSupported) + { + // Do something + } + else + { + ArmBase.IntrinsicMethod(); + } +} +``` +The behavior of the `BypassReadyToRunForIntrinsicsHelperUse` is that 1 or more attributes may be applied to a given method. If any of the types specified via the attribute will not have an invariant result for its associated `IsSupported` property at runtime, then the method will not be compiled or inlined into another function during R2R compilation. If no type so described will have a true result for the `IsSupported` method, then the method will not be compiled or inlined into another function during R2R compilation. # Mechanisms in the JIT to generate correct code to handle varied instruction set support The JIT receives flags which instruct it on what instruction sets are valid to use, and has access to a new jit interface api `notifyInstructionSetUsage(isa, bool supportBehaviorRequired)`. diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoInstructionSet.cs b/src/coreclr/tools/Common/JitInterface/CorInfoInstructionSet.cs index 73538c68172891..df6c06206bf84d 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoInstructionSet.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoInstructionSet.cs @@ -1334,4 +1334,369 @@ public void Set64BitInstructionSetVariantsUnconditionally(TargetArchitecture arc } } } + public static class InstructionSetParser + { + public static InstructionSet LookupPlatformIntrinsicInstructionSet(TargetArchitecture targetArch, TypeDesc intrinsicType) + { + MetadataType metadataType = intrinsicType.GetTypeDefinition() as MetadataType; + if (metadataType == null) + return InstructionSet.ILLEGAL; + + string namespaceName; + string typeName = metadataType.Name; + string nestedTypeName = null; + if (metadataType.ContainingType != null) + { + var enclosingType = (MetadataType)metadataType.ContainingType; + namespaceName = enclosingType.Namespace; + nestedTypeName = metadataType.Name; + typeName = enclosingType.Name; + } + else + { + namespaceName = metadataType.Namespace; + } + + string platformIntrinsicNamespace; + + switch (targetArch) + { + case TargetArchitecture.ARM64: + platformIntrinsicNamespace = "System.Runtime.Intrinsics.Arm"; + break; + + case TargetArchitecture.X64: + case TargetArchitecture.X86: + platformIntrinsicNamespace = "System.Runtime.Intrinsics.X86"; + break; + + default: + return InstructionSet.ILLEGAL; + } + + if (namespaceName != platformIntrinsicNamespace) + return InstructionSet.ILLEGAL; + + switch (targetArch) + { + + case TargetArchitecture.ARM64: + switch (typeName) + { + + case "ArmBase": + if (nestedTypeName == "Arm64") + { return InstructionSet.ARM64_ArmBase_Arm64; } + else + { return InstructionSet.ARM64_ArmBase; } + + case "AdvSimd": + if (nestedTypeName == "Arm64") + { return InstructionSet.ARM64_AdvSimd_Arm64; } + else + { return InstructionSet.ARM64_AdvSimd; } + + case "Aes": + if (nestedTypeName == "Arm64") + { return InstructionSet.ARM64_Aes_Arm64; } + else + { return InstructionSet.ARM64_Aes; } + + case "Crc32": + if (nestedTypeName == "Arm64") + { return InstructionSet.ARM64_Crc32_Arm64; } + else + { return InstructionSet.ARM64_Crc32; } + + case "Dp": + if (nestedTypeName == "Arm64") + { return InstructionSet.ARM64_Dp_Arm64; } + else + { return InstructionSet.ARM64_Dp; } + + case "Rdm": + if (nestedTypeName == "Arm64") + { return InstructionSet.ARM64_Rdm_Arm64; } + else + { return InstructionSet.ARM64_Rdm; } + + case "Sha1": + if (nestedTypeName == "Arm64") + { return InstructionSet.ARM64_Sha1_Arm64; } + else + { return InstructionSet.ARM64_Sha1; } + + case "Sha256": + if (nestedTypeName == "Arm64") + { return InstructionSet.ARM64_Sha256_Arm64; } + else + { return InstructionSet.ARM64_Sha256; } + + } + break; + + case TargetArchitecture.X64: + switch (typeName) + { + + case "X86Base": + if (nestedTypeName == "X64") + { return InstructionSet.X64_X86Base_X64; } + else + { return InstructionSet.X64_X86Base; } + + case "Sse": + if (nestedTypeName == "X64") + { return InstructionSet.X64_SSE_X64; } + else + { return InstructionSet.X64_SSE; } + + case "Sse2": + if (nestedTypeName == "X64") + { return InstructionSet.X64_SSE2_X64; } + else + { return InstructionSet.X64_SSE2; } + + case "Sse3": + if (nestedTypeName == "X64") + { return InstructionSet.X64_SSE3_X64; } + else + { return InstructionSet.X64_SSE3; } + + case "Ssse3": + if (nestedTypeName == "X64") + { return InstructionSet.X64_SSSE3_X64; } + else + { return InstructionSet.X64_SSSE3; } + + case "Sse41": + if (nestedTypeName == "X64") + { return InstructionSet.X64_SSE41_X64; } + else + { return InstructionSet.X64_SSE41; } + + case "Sse42": + if (nestedTypeName == "X64") + { return InstructionSet.X64_SSE42_X64; } + else + { return InstructionSet.X64_SSE42; } + + case "Avx": + if (nestedTypeName == "X64") + { return InstructionSet.X64_AVX_X64; } + else + { return InstructionSet.X64_AVX; } + + case "Avx2": + if (nestedTypeName == "X64") + { return InstructionSet.X64_AVX2_X64; } + else + { return InstructionSet.X64_AVX2; } + + case "Aes": + if (nestedTypeName == "X64") + { return InstructionSet.X64_AES_X64; } + else + { return InstructionSet.X64_AES; } + + case "Bmi1": + if (nestedTypeName == "X64") + { return InstructionSet.X64_BMI1_X64; } + else + { return InstructionSet.X64_BMI1; } + + case "Bmi2": + if (nestedTypeName == "X64") + { return InstructionSet.X64_BMI2_X64; } + else + { return InstructionSet.X64_BMI2; } + + case "Fma": + if (nestedTypeName == "X64") + { return InstructionSet.X64_FMA_X64; } + else + { return InstructionSet.X64_FMA; } + + case "Lzcnt": + if (nestedTypeName == "X64") + { return InstructionSet.X64_LZCNT_X64; } + else + { return InstructionSet.X64_LZCNT; } + + case "Pclmulqdq": + if (nestedTypeName == "X64") + { return InstructionSet.X64_PCLMULQDQ_X64; } + else + { return InstructionSet.X64_PCLMULQDQ; } + + case "Popcnt": + if (nestedTypeName == "X64") + { return InstructionSet.X64_POPCNT_X64; } + else + { return InstructionSet.X64_POPCNT; } + + case "AvxVnni": + if (nestedTypeName == "X64") + { return InstructionSet.X64_AVXVNNI_X64; } + else + { return InstructionSet.X64_AVXVNNI; } + + case "Movbe": + if (nestedTypeName == "X64") + { return InstructionSet.X64_MOVBE_X64; } + else + { return InstructionSet.X64_MOVBE; } + + case "X86Serialize": + if (nestedTypeName == "X64") + { return InstructionSet.X64_X86Serialize_X64; } + else + { return InstructionSet.X64_X86Serialize; } + + case "Avx512F": + if (nestedTypeName == "X64") + { return InstructionSet.X64_AVX512F_X64; } + else + if (nestedTypeName == "VL") + { return InstructionSet.X64_AVX512F_VL; } + else + { return InstructionSet.X64_AVX512F; } + + case "Avx512BW": + if (nestedTypeName == "X64") + { return InstructionSet.X64_AVX512BW_X64; } + else + if (nestedTypeName == "VL") + { return InstructionSet.X64_AVX512BW_VL; } + else + { return InstructionSet.X64_AVX512BW; } + + case "Avx512CD": + if (nestedTypeName == "X64") + { return InstructionSet.X64_AVX512CD_X64; } + else + if (nestedTypeName == "VL") + { return InstructionSet.X64_AVX512CD_VL; } + else + { return InstructionSet.X64_AVX512CD; } + + case "Avx512DQ": + if (nestedTypeName == "X64") + { return InstructionSet.X64_AVX512DQ_X64; } + else + if (nestedTypeName == "VL") + { return InstructionSet.X64_AVX512DQ_VL; } + else + { return InstructionSet.X64_AVX512DQ; } + + case "Avx512Vbmi": + if (nestedTypeName == "X64") + { return InstructionSet.X64_AVX512VBMI_X64; } + else + if (nestedTypeName == "VL") + { return InstructionSet.X64_AVX512VBMI_VL; } + else + { return InstructionSet.X64_AVX512VBMI; } + + } + break; + + case TargetArchitecture.X86: + switch (typeName) + { + + case "X86Base": + { return InstructionSet.X86_X86Base; } + + case "Sse": + { return InstructionSet.X86_SSE; } + + case "Sse2": + { return InstructionSet.X86_SSE2; } + + case "Sse3": + { return InstructionSet.X86_SSE3; } + + case "Ssse3": + { return InstructionSet.X86_SSSE3; } + + case "Sse41": + { return InstructionSet.X86_SSE41; } + + case "Sse42": + { return InstructionSet.X86_SSE42; } + + case "Avx": + { return InstructionSet.X86_AVX; } + + case "Avx2": + { return InstructionSet.X86_AVX2; } + + case "Aes": + { return InstructionSet.X86_AES; } + + case "Bmi1": + { return InstructionSet.X86_BMI1; } + + case "Bmi2": + { return InstructionSet.X86_BMI2; } + + case "Fma": + { return InstructionSet.X86_FMA; } + + case "Lzcnt": + { return InstructionSet.X86_LZCNT; } + + case "Pclmulqdq": + { return InstructionSet.X86_PCLMULQDQ; } + + case "Popcnt": + { return InstructionSet.X86_POPCNT; } + + case "AvxVnni": + { return InstructionSet.X86_AVXVNNI; } + + case "Movbe": + { return InstructionSet.X86_MOVBE; } + + case "X86Serialize": + { return InstructionSet.X86_X86Serialize; } + + case "Avx512F": + if (nestedTypeName == "VL") + { return InstructionSet.X86_AVX512F_VL; } + else + { return InstructionSet.X86_AVX512F; } + + case "Avx512BW": + if (nestedTypeName == "VL") + { return InstructionSet.X86_AVX512BW_VL; } + else + { return InstructionSet.X86_AVX512BW; } + + case "Avx512CD": + if (nestedTypeName == "VL") + { return InstructionSet.X86_AVX512CD_VL; } + else + { return InstructionSet.X86_AVX512CD; } + + case "Avx512DQ": + if (nestedTypeName == "VL") + { return InstructionSet.X86_AVX512DQ_VL; } + else + { return InstructionSet.X86_AVX512DQ; } + + case "Avx512Vbmi": + if (nestedTypeName == "VL") + { return InstructionSet.X86_AVX512VBMI_VL; } + else + { return InstructionSet.X86_AVX512VBMI; } + + } + break; + + } + return InstructionSet.ILLEGAL; + } + } } diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetDesc.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetDesc.txt index fa4107640b494a..d5f74271ed13fb 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetDesc.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetDesc.txt @@ -23,7 +23,7 @@ ; DO NOT CHANGE R2R NUMERIC VALUES OF THE EXISTING SETS. Changing R2R numeric values definitions would be R2R format breaking change. ; Definition of X86 instruction sets -definearch ,X86 ,32Bit ,X64 +definearch ,X86 ,32Bit ,X64, X64 instructionset ,X86 ,X86Base , ,22 ,X86Base ,base instructionset ,X86 ,Sse , ,1 ,SSE ,sse @@ -126,12 +126,12 @@ implication ,X86 ,AVX512VBMI ,AVX512BW implication ,X86 ,AVX512VBMI_VL ,AVX512BW_VL ; Definition of X64 instruction sets -definearch ,X64 ,64Bit ,X64 +definearch ,X64 ,64Bit ,X64, X64 copyinstructionsets,X86 ,X64 ; Definition of Arm64 instruction sets -definearch ,ARM64 ,64Bit ,Arm64 +definearch ,ARM64 ,64Bit ,Arm64, Arm64 instructionset ,ARM64 ,ArmBase , ,16 ,ArmBase ,base instructionset ,ARM64 ,AdvSimd , ,17 ,AdvSimd ,neon diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetGenerator.cs b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetGenerator.cs index bc218f33d34b59..4547b91e2fdc70 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetGenerator.cs +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetGenerator.cs @@ -92,6 +92,7 @@ public InstructionSetImplication(string architecture, InstructionSetImplication private Dictionary> _architectureVectorInstructionSetJitNames = new Dictionary>(); private HashSet _64BitArchitectures = new HashSet(); private Dictionary _64BitVariantArchitectureJitNameSuffix = new Dictionary(); + private Dictionary _64BitVariantArchitectureManagedNameSuffix = new Dictionary(); // This represents the number of flags fields we currently track private const int FlagsFieldCount = 1; @@ -126,6 +127,11 @@ private string ArchToInstructionSetSuffixArch(string arch) return _64BitVariantArchitectureJitNameSuffix[arch]; } + private string ArchToManagedInstructionSetSuffixArch(string arch) + { + return _64BitVariantArchitectureManagedNameSuffix[arch]; + } + public bool ParseInput(TextReader tr) { int currentLineIndex = 1; @@ -151,7 +157,7 @@ public bool ParseInput(TextReader tr) switch (command[0]) { case "definearch": - if (command.Length != 4) + if (command.Length != 5) throw new Exception($"Incorrect number of args for definearch {command.Length}"); ArchitectureEncountered(command[1]); if (command[2] == "64Bit") @@ -163,6 +169,7 @@ public bool ParseInput(TextReader tr) throw new Exception("Architecture must be 32Bit or 64Bit"); } _64BitVariantArchitectureJitNameSuffix[command[1]] = command[3]; + _64BitVariantArchitectureManagedNameSuffix[command[1]] = command[4]; break; case "instructionset": if (command.Length != 7) @@ -769,6 +776,119 @@ public void Set64BitInstructionSetVariantsUnconditionally(TargetArchitecture arc tr.Write(@" } } } + public static class InstructionSetParser + { + public static InstructionSet LookupPlatformIntrinsicInstructionSet(TargetArchitecture targetArch, TypeDesc intrinsicType) + { + MetadataType metadataType = intrinsicType.GetTypeDefinition() as MetadataType; + if (metadataType == null) + return InstructionSet.ILLEGAL; + + string namespaceName; + string typeName = metadataType.Name; + string nestedTypeName = null; + if (metadataType.ContainingType != null) + { + var enclosingType = (MetadataType)metadataType.ContainingType; + namespaceName = enclosingType.Namespace; + nestedTypeName = metadataType.Name; + typeName = enclosingType.Name; + } + else + { + namespaceName = metadataType.Namespace; + } + + string platformIntrinsicNamespace; + + switch (targetArch) + { + case TargetArchitecture.ARM64: + platformIntrinsicNamespace = ""System.Runtime.Intrinsics.Arm""; + break; + + case TargetArchitecture.X64: + case TargetArchitecture.X86: + platformIntrinsicNamespace = ""System.Runtime.Intrinsics.X86""; + break; + + default: + return InstructionSet.ILLEGAL; + } + + if (namespaceName != platformIntrinsicNamespace) + return InstructionSet.ILLEGAL; + + switch (targetArch) + { +"); + foreach (string architecture in _architectures) + { + tr.Write($@" + case TargetArchitecture.{architecture}: + switch (typeName) + {{ +"); + foreach (var instructionSet in _instructionSets) + { + if (instructionSet.Architecture != architecture) continue; + // VL instructionSets are handled as part of their master instruction set. + if (instructionSet.ManagedName.EndsWith("_VL")) + continue; + + // Instruction sets without a managed name are not handled here. + if (string.IsNullOrEmpty(instructionSet.ManagedName)) + continue; + + InstructionSetInfo vlInstructionSet = null; + foreach (var potentialVLinstructionSet in _instructionSets) + { + if (instructionSet.Architecture != architecture) continue; + string managedName = potentialVLinstructionSet.ManagedName; + if (managedName.EndsWith("_VL") && instructionSet.ManagedName == managedName.Substring(0, managedName.Length - 3)) + { + vlInstructionSet = potentialVLinstructionSet; break; + } + } + + string hasSixtyFourBitInstructionSet = null; + if (_64bitVariants[architecture].Contains(instructionSet.JitName) && _64BitArchitectures.Contains(architecture)) + { + hasSixtyFourBitInstructionSet = ArchToManagedInstructionSetSuffixArch(architecture); + } + + tr.Write(@$" + case ""{instructionSet.ManagedName}"":"); + + if (hasSixtyFourBitInstructionSet != null) + { + tr.Write($@" + if (nestedTypeName == ""{hasSixtyFourBitInstructionSet}"") + {{ return InstructionSet.{architecture}_{instructionSet.JitName}_{ArchToInstructionSetSuffixArch(architecture)}; }} + else"); + } + if (vlInstructionSet != null) + { + tr.Write($@" + if (nestedTypeName == ""VL"") + {{ return InstructionSet.{architecture}_{vlInstructionSet.JitName}; }} + else"); + } + tr.Write($@" + {{ return InstructionSet.{architecture}_{instructionSet.JitName}; }} +"); + } + tr.Write($@" + }} + break; +"); + } + + tr.Write(@" + } + return InstructionSet.ILLEGAL; + } + } } "); return; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs index cb4532ac16705e..36cd378ba90e5f 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs @@ -15,6 +15,7 @@ using Internal.TypeSystem.Interop; using Debug = System.Diagnostics.Debug; using Internal.ReadyToRunConstants; +using Internal.JitInterface; namespace ILCompiler { @@ -30,6 +31,7 @@ public class ReadyToRunCompilationModuleGroupConfig public IEnumerable CrossModuleInlineable; public bool CompileGenericDependenciesFromVersionBubbleModuleSet; public bool CompileAllPossibleCrossModuleCode; + public InstructionSetSupport InstructionSetSupport; } public abstract class ReadyToRunCompilationModuleGroupBase : CompilationModuleGroup @@ -64,9 +66,11 @@ public abstract class ReadyToRunCompilationModuleGroupBase : CompilationModuleGr private ConcurrentDictionary _tokenTranslationFreeNonVersionable = new ConcurrentDictionary(); private readonly Func _tokenTranslationFreeNonVersionableUncached; private bool CompileAllPossibleCrossModuleCode = false; + private InstructionSetSupport _instructionSetSupport; public ReadyToRunCompilationModuleGroupBase(ReadyToRunCompilationModuleGroupConfig config) { + _instructionSetSupport = config.InstructionSetSupport; _compilationModuleSet = new HashSet(config.CompilationModuleSet); _isCompositeBuildMode = config.IsCompositeBuildMode; _isInputBubble = config.IsInputBubble; @@ -409,6 +413,11 @@ public sealed override bool CanInline(MethodDesc callerMethod, MethodDesc callee bool canInline = (VersionsWithMethodBody(callerMethod) || CrossModuleInlineable(callerMethod)) && (VersionsWithMethodBody(calleeMethod) || CrossModuleInlineable(calleeMethod) || IsNonVersionableWithILTokensThatDoNotNeedTranslation(calleeMethod)); + if (canInline) + { + if (!CorInfoImpl.ShouldCodeNotBeCompiledIntoFinalImage(_instructionSetSupport, calleeMethod)) + canInline = false; + } return canInline; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunLibraryRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunLibraryRootProvider.cs index 838cf6e9df6c00..ff62a907bde2cb 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunLibraryRootProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunLibraryRootProvider.cs @@ -60,7 +60,7 @@ private void RootMethods(MetadataType type, string reason, IRootingServiceProvid try { - if (!CorInfoImpl.ShouldSkipCompilation(method)) + if (!CorInfoImpl.ShouldSkipCompilation(null, method)) { CheckCanGenerateMethod(methodToRoot); rootProvider.AddCompilationRoot(methodToRoot, rootMinimalDependencies: false, reason: reason); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProfilingRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProfilingRootProvider.cs index f7175cf6ddead4..1dc1dce5a1b010 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProfilingRootProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProfilingRootProvider.cs @@ -61,7 +61,7 @@ public void AddCompilationRoots(IRootingServiceProvider rootProvider) if (containsSignatureVariables) continue; - if (!CorInfoImpl.ShouldSkipCompilation(method)) + if (!CorInfoImpl.ShouldSkipCompilation(null, method)) { ReadyToRunLibraryRootProvider.CheckCanGenerateMethod(method); rootProvider.AddCompilationRoot(method, rootMinimalDependencies: true, reason: "Profile triggered method"); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunVisibilityRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunVisibilityRootProvider.cs index 4766937ce694b7..31ab3dcb2c1245 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunVisibilityRootProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunVisibilityRootProvider.cs @@ -102,7 +102,7 @@ private void RootMethods(MetadataType type, string reason, IRootingServiceProvid try { - if (!CorInfoImpl.ShouldSkipCompilation(method)) + if (!CorInfoImpl.ShouldSkipCompilation(null, method)) { ReadyToRunLibraryRootProvider.CheckCanGenerateMethod(methodToRoot); rootProvider.AddCompilationRoot(methodToRoot, rootMinimalDependencies: false, reason: reason); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunXmlRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunXmlRootProvider.cs index 8d168a3a39f2d0..f952060d9500a5 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunXmlRootProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunXmlRootProvider.cs @@ -141,7 +141,7 @@ private void RootMethod(MethodDesc method) try { - if (!CorInfoImpl.ShouldSkipCompilation(method)) + if (!CorInfoImpl.ShouldSkipCompilation(null, method)) { ReadyToRunLibraryRootProvider.CheckCanGenerateMethod(methodToRoot); _rootingServiceProvider.AddCompilationRoot(methodToRoot, rootMinimalDependencies: false, reason: "Linker XML descriptor"); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index e0617683dce460..2f2259a30dbfb2 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -493,7 +493,7 @@ private static mdToken FindGenericMethodArgTypeSpec(EcmaModule module) throw new NotSupportedException(); } - public static bool ShouldSkipCompilation(MethodDesc methodNeedingCode) + public static bool ShouldSkipCompilation(InstructionSetSupport instructionSetSupport, MethodDesc methodNeedingCode) { if (methodNeedingCode.IsAggressiveOptimization) { @@ -520,15 +520,89 @@ public static bool ShouldSkipCompilation(MethodDesc methodNeedingCode) // Special methods on delegate types return true; } - if (methodNeedingCode.HasCustomAttribute("System.Runtime", "BypassReadyToRunAttribute")) + if (ShouldCodeNotBeCompiledIntoFinalImage(instructionSetSupport, methodNeedingCode)) { - // This is a quick workaround to opt specific methods out of ReadyToRun compilation to work around bugs. return true; } return false; } + public static bool ShouldCodeNotBeCompiledIntoFinalImage(InstructionSetSupport instructionSetSupport, MethodDesc method) + { + EcmaMethod ecmaMethod = method.GetTypicalMethodDefinition() as EcmaMethod; + + var metadataReader = ecmaMethod.MetadataReader; + var stringComparer = metadataReader.StringComparer; + + var handle = ecmaMethod.Handle; + + List bypassForHelperIntrinsicsList = null; + + foreach (var attributeHandle in metadataReader.GetMethodDefinition(handle).GetCustomAttributes()) + { + StringHandle namespaceHandle, nameHandle; + if (!metadataReader.GetAttributeNamespaceAndName(attributeHandle, out namespaceHandle, out nameHandle)) + continue; + + if (metadataReader.StringComparer.Equals(namespaceHandle, "System.Runtime")) + { + if (metadataReader.StringComparer.Equals(nameHandle, "BypassReadyToRunAttribute")) + { + return true; + } + if (metadataReader.StringComparer.Equals(nameHandle, "BypassReadyToRunForIntrinsicsHelperUseAttribute")) + { + var customAttribute = metadataReader.GetCustomAttribute(attributeHandle); + var typeProvider = new CustomAttributeTypeProvider(ecmaMethod.Module); + var fixedArguments = customAttribute.DecodeValue(typeProvider).FixedArguments; + if (fixedArguments.Length < 1) + continue; + + TypeDesc typeForBypass = fixedArguments[0].Value as TypeDesc; + if (typeForBypass != null) + { + if (bypassForHelperIntrinsicsList == null) + bypassForHelperIntrinsicsList = new List(); + + bypassForHelperIntrinsicsList.Add(typeForBypass); + } + } + } + } + + if (instructionSetSupport != null && bypassForHelperIntrinsicsList != null && bypassForHelperIntrinsicsList.Count > 0) + { + // Default to true, and set to false if at least of the types is actually supported in the current environment, and none of the + // intrinisc types are in an opportunistic state. + bool doBypass = true; + + foreach (var intrinsicType in bypassForHelperIntrinsicsList) + { + InstructionSet instructionSet = InstructionSetParser.LookupPlatformIntrinsicInstructionSet(intrinsicType.Context.Target.Architecture, intrinsicType); + if (instructionSet == InstructionSet.ILLEGAL) + { + // This instruction set isn't supported on the current platform at all. + continue; + } + if (instructionSetSupport.IsInstructionSetSupported(instructionSet) || instructionSetSupport.IsInstructionSetExplicitlyUnsupported(instructionSet)) + { + doBypass = false; + } + else + { + // If we reach here this is an instruction set generally supported on this platform, but we don't know what the behavior will be at runtime + return true; + } + } + + return doBypass; + } + + // No reason to bypass compilation and code generation. + return false; + } + private static bool FunctionJustThrows(MethodIL ilBody) { try @@ -582,7 +656,7 @@ private static bool FunctionHasNonReferenceableTypedILCatchClause(MethodIL metho public static bool IsMethodCompilable(Compilation compilation, MethodDesc method) { // This logic must mirror the logic in CompileMethod used to get to the point of calling CompileMethodInternal - if (ShouldSkipCompilation(method) || MethodSignatureIsUnstable(method.Signature, out var _)) + if (ShouldSkipCompilation(compilation.InstructionSetSupport, method) || MethodSignatureIsUnstable(method.Signature, out var _)) return false; MethodIL methodIL = compilation.GetMethodIL(method); @@ -605,7 +679,7 @@ public void CompileMethod(MethodWithGCInfo methodCodeNodeNeedingCode, Logger log try { - if (ShouldSkipCompilation(MethodBeingCompiled)) + if (ShouldSkipCompilation(_compilation.InstructionSetSupport, MethodBeingCompiled)) { if (logger.IsVerbose) logger.Writer.WriteLine($"Info: Method `{MethodBeingCompiled}` was not compiled because it is skipped."); diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index b044eea0b47438..2eb91173d9f04f 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -402,6 +402,7 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru groupConfig.CrossModuleInlining = groupConfig.CrossModuleGenericCompilation; // Currently we set these flags to the same values groupConfig.CrossModuleInlineable = crossModuleInlineableCode; groupConfig.CompileAllPossibleCrossModuleCode = false; + groupConfig.InstructionSetSupport = instructionSetSupport; // Handle non-local generics command line option ModuleDesc nonLocalGenericsHome = compileBubbleGenerics ? inputModules[0] : null; diff --git a/src/libraries/Common/src/System/HexConverter.cs b/src/libraries/Common/src/System/HexConverter.cs index 621db00e023396..992849daa4cc7e 100644 --- a/src/libraries/Common/src/System/HexConverter.cs +++ b/src/libraries/Common/src/System/HexConverter.cs @@ -237,6 +237,7 @@ public static bool TryDecodeFromUtf16(ReadOnlySpan chars, Span bytes #if SYSTEM_PRIVATE_CORELIB [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] public static bool TryDecodeFromUtf16_Vector128(ReadOnlySpan chars, Span bytes) { Debug.Assert(Ssse3.IsSupported || AdvSimd.Arm64.IsSupported); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs index ae6476746c9c75..91ff0af94f7723 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs @@ -2444,6 +2444,8 @@ public static Vector128 Shuffle(Vector128 vector, Vector128 /// On hardware with support, indices are treated as modulo 16, and if the high bit is set, the result will be set to 0 for that element. /// On hardware with or support, this method behaves the same as Shuffle. [MethodImpl(MethodImplOptions.AggressiveInlining)] + [BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] // Because this method has specific behavior when this is used on a machine with SSSE3, mark with this attribute which will + // make the behavior of the function at runtime not be affected by R2R compilation behavior internal static Vector128 ShuffleUnsafe(Vector128 vector, Vector128 indices) { if (Ssse3.IsSupported) From 59a55e4b8a208b81515eff1c061c0dc207b80bbd Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 27 Apr 2023 14:00:47 -0700 Subject: [PATCH 04/19] Fix build break --- .../src/System/Runtime/Intrinsics/Vector128.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs index 91ff0af94f7723..90a78e6d646432 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs @@ -2444,7 +2444,7 @@ public static Vector128 Shuffle(Vector128 vector, Vector128 /// On hardware with support, indices are treated as modulo 16, and if the high bit is set, the result will be set to 0 for that element. /// On hardware with or support, this method behaves the same as Shuffle. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] // Because this method has specific behavior when this is used on a machine with SSSE3, mark with this attribute which will + [BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] // Because this method has specific behavior when this is used on a machine with SSSE3, mark with this attribute which will // make the behavior of the function at runtime not be affected by R2R compilation behavior internal static Vector128 ShuffleUnsafe(Vector128 vector, Vector128 indices) { From 768d1a94f10938b616b17c3a04f21a26cd10b1da Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 28 Apr 2023 12:37:05 -0700 Subject: [PATCH 05/19] Fix condition noted in code review --- .../Compiler/ReadyToRunCompilationModuleGroupBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs index 36cd378ba90e5f..27d0a9798b0d61 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs @@ -415,7 +415,7 @@ public sealed override bool CanInline(MethodDesc callerMethod, MethodDesc callee if (canInline) { - if (!CorInfoImpl.ShouldCodeNotBeCompiledIntoFinalImage(_instructionSetSupport, calleeMethod)) + if (CorInfoImpl.ShouldCodeNotBeCompiledIntoFinalImage(_instructionSetSupport, calleeMethod)) canInline = false; } return canInline; From 5ac974c8e89ea38a09e448d670df498c4ea3f74e Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 8 May 2023 17:17:10 -0700 Subject: [PATCH 06/19] Update analyzer to detect more patterns, and attribute much of System.Private.CoreLib --- .../Common/src/System/HexConverter.cs | 4 + ...ntrinsicsInSystemPrivateCoreLibAnalyzer.cs | 337 +++++++++++++----- .../src/System/Buffers/Text/Base64Decoder.cs | 2 + .../System.Private.CoreLib/src/System/Guid.cs | 2 + .../IndexOfAnyAsciiSearcher.cs | 6 +- .../IndexOfAnyValues/ProbabilisticMap.cs | 4 + .../src/System/Text/Ascii.Equality.cs | 3 +- .../src/System/Text/Ascii.cs | 3 +- .../src/System/Text/Latin1Utility.cs | 18 + .../Text/Unicode/Utf16Utility.Validation.cs | 2 +- 10 files changed, 284 insertions(+), 97 deletions(-) diff --git a/src/libraries/Common/src/System/HexConverter.cs b/src/libraries/Common/src/System/HexConverter.cs index 992849daa4cc7e..6e4571d7403989 100644 --- a/src/libraries/Common/src/System/HexConverter.cs +++ b/src/libraries/Common/src/System/HexConverter.cs @@ -91,6 +91,8 @@ public static void ToCharsBuffer(byte value, Span buffer, int startingInde #if SYSTEM_PRIVATE_CORELIB // Converts Vector128 into 2xVector128 ASCII Hex representation [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] internal static (Vector128, Vector128) AsciiToHexVector128(Vector128 src, Vector128 hexMap) { Debug.Assert(Ssse3.IsSupported || AdvSimd.Arm64.IsSupported); @@ -105,6 +107,8 @@ internal static (Vector128, Vector128) AsciiToHexVector128(Vector128 Vector128.ShuffleUnsafe(hexMap, highNibbles & Vector128.Create((byte)0xF))); } + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] private static void EncodeToUtf16_Vector128(ReadOnlySpan bytes, Span chars, Casing casing) { Debug.Assert(bytes.Length >= Vector128.Count); diff --git a/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs b/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs index 8be3fc9bda0ab2..befa51d9101234 100644 --- a/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs +++ b/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Data.Common; using System.Diagnostics; using System.Linq; using Microsoft.CodeAnalysis; @@ -28,17 +29,25 @@ public class IntrinsicsInSystemPrivateCoreLibAnalyzer : DiagnosticAnalyzer // You can change these strings in the Resources.resx file. If you do not want your analyzer to be localize-able, you can use regular strings for Title and MessageFormat. // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/Localizing%20Analyzers.md for more on localization private const string Title = "System.Private.CoreLib ReadyToRun Intrinsics"; - private const string MessageFormat = "Intrinsics from class '{0}' used without the protection of an explicit if statement checking the correct IsSupported flag"; + private const string MessageFormat = "Intrinsics from class '{0}' used without the protection of an explicit if statement checking the correct IsSupported flag or BypassReadyToRunForIntrinsicsHelperUse"; private const string Description = "ReadyToRun Intrinsic Safety For System.Private.CoreLib."; private const string Category = "IntrinsicsCorrectness"; private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description); + public const string DiagnosticIdHelper = "IntrinsicsInSystemPrivateCoreLibHelper"; + private const string MessageHelperFormat = "Helper '{0}' used without the protection of an explicit if statement checking the correct IsSupported flag or BypassReadyToRunForIntrinsicsHelperUse"; + private static readonly DiagnosticDescriptor RuleHelper = new DiagnosticDescriptor(DiagnosticIdHelper, Title, MessageHelperFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description); + public const string DiagnosticIdConditionParsing = "IntrinsicsInSystemPrivateCoreLibConditionParsing"; private const string MessageNonParseableConditionFormat = "Unable to parse condition to determine if intrinsics are correctly used"; private static readonly DiagnosticDescriptor RuleCantParse = new DiagnosticDescriptor(DiagnosticIdConditionParsing, Title, MessageNonParseableConditionFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description); - public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(Rule, RuleCantParse); } } + public const string DiagnosticIdAttributeNotSpecificEnough = "IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough"; + private const string MessageAttributeNotSpecificEnoughFormat = "BypassReadyToRunForIntrinsicsHelperUse({0}) attribute found which relates to this IsSupported check, but is not specific enough. Suppress this error if this function has an appropriate if condition so that if the meaning of the function is invariant regardless of the result of the call to IsSupported."; + private static readonly DiagnosticDescriptor RuleAttributeNotSpecificEnough = new DiagnosticDescriptor(DiagnosticIdAttributeNotSpecificEnough, Title, MessageAttributeNotSpecificEnoughFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description); + + public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(Rule, RuleHelper, RuleCantParse, RuleAttributeNotSpecificEnough); } } private static INamespaceSymbol GetNamespace(IAssemblySymbol assembly, params string[] namespaceNames) { @@ -110,7 +119,7 @@ private static IEnumerable GetSubtypes(INamespaceSymbol namesp private sealed class IntrinsicsAnalyzerOnLoadData { - public IntrinsicsAnalyzerOnLoadData(List namedTypesToBeProtected, + public IntrinsicsAnalyzerOnLoadData(HashSet namedTypesToBeProtected, INamedTypeSymbol? bypassReadyToRunAttribute, INamedTypeSymbol? bypassReadyToRunForIntrinsicsHelperUse) { @@ -118,7 +127,7 @@ public IntrinsicsAnalyzerOnLoadData(List namedTypesToBeProtect BypassReadyToRunAttribute = bypassReadyToRunAttribute; BypassReadyToRunForIntrinsicsHelperUse = bypassReadyToRunForIntrinsicsHelperUse; } - public readonly List NamedTypesToBeProtected; + public readonly HashSet NamedTypesToBeProtected; public readonly INamedTypeSymbol? BypassReadyToRunAttribute; public readonly INamedTypeSymbol? BypassReadyToRunForIntrinsicsHelperUse; } @@ -129,7 +138,7 @@ public override void Initialize(AnalysisContext context) context.EnableConcurrentExecution(); context.RegisterCompilationStartAction(context => { - List namedTypesToBeProtected = new List(); + HashSet namedTypesToBeProtected = new HashSet(SymbolEqualityComparer.Default); INamespaceSymbol systemRuntimeIntrinsicsNamespace = GetNamespace(context.Compilation.Assembly, "System", "Runtime", "Intrinsics"); INamedTypeSymbol? bypassReadyToRunAttribute = context.Compilation.Assembly.GetTypeByMetadataName("System.Runtime.BypassReadyToRunAttribute"); INamedTypeSymbol? bypassReadyToRunForIntrinsicsHelperUse = context.Compilation.Assembly.GetTypeByMetadataName("System.Runtime.BypassReadyToRunForIntrinsicsHelperUseAttribute"); @@ -175,7 +184,7 @@ public override void Initialize(AnalysisContext context) { AnalyzeOperation(context.Operation, methodSymbol, context, onLoadData); }, - OperationKind.Invocation); + OperationKind.Invocation, OperationKind.PropertyReference); }, SymbolKind.Method); }); } @@ -201,6 +210,16 @@ public override void Initialize(AnalysisContext context) } } + private static INamedTypeSymbol? GetIsSupportedTypeSymbol(SemanticModel model, IdentifierNameSyntax identifierName) + { + var symbolInfo = model.GetSymbolInfo(identifierName); + + if (identifierName.Identifier.Text == "IsSupported") + return symbolInfo.Symbol.ContainingSymbol as INamedTypeSymbol; + else + return null; + } + private static INamedTypeSymbol[] GatherAndConditions(SemanticModel model, ExpressionSyntax expressionToDecompose) { if (expressionToDecompose is ParenthesizedExpressionSyntax parenthesizedExpression) @@ -218,6 +237,16 @@ private static INamedTypeSymbol[] GatherAndConditions(SemanticModel model, Expre else return new INamedTypeSymbol[] { isSupportedType }; } + else if (expressionToDecompose is IdentifierNameSyntax identifier) + { + var isSupportedType = GetIsSupportedTypeSymbol(model, identifier); + if (isSupportedType == null) + { + return Array.Empty(); + } + else + return new INamedTypeSymbol[] { isSupportedType }; + } else if (expressionToDecompose is BinaryExpressionSyntax binaryExpression) { if (binaryExpression.OperatorToken is SyntaxToken operatorToken && operatorToken.ValueText == "&&") @@ -243,36 +272,72 @@ private static INamedTypeSymbol[] GatherAndConditions(SemanticModel model, Expre return Array.Empty(); } - private static INamedTypeSymbol[][] DecomposeConditionForIsSupportedGroups(SemanticModel model, ExpressionSyntax expressionToDecompose) + private static INamedTypeSymbol[][] DecomposePropertySymbolForIsSupportedGroups_Property(OperationAnalysisContext context, SemanticModel model, ExpressionSyntax expressionToDecompose) { - if (expressionToDecompose is ParenthesizedExpressionSyntax parenthesizedExpression) + var symbolInfo = model.GetSymbolInfo(expressionToDecompose); + if (symbolInfo.Symbol.Kind != SymbolKind.Property) { - return DecomposeConditionForIsSupportedGroups(model, parenthesizedExpression.Expression); + return Array.Empty(); } - if (expressionToDecompose is MemberAccessExpressionSyntax memberAccessExpression) + + if (symbolInfo.Symbol.Name == "IsSupported") { - var isSupportedType = GetIsSupportedTypeSymbol(model, memberAccessExpression); - if (isSupportedType == null) + var typeSymbol = symbolInfo.Symbol.ContainingSymbol as INamedTypeSymbol; + if (typeSymbol != null) { - return Array.Empty(); + return new INamedTypeSymbol[][] { new INamedTypeSymbol[] { typeSymbol } }; } - else + } + + var propertyDefiningSyntax = symbolInfo.Symbol.DeclaringSyntaxReferences[0].GetSyntax(); + if (propertyDefiningSyntax != null) + { + if (propertyDefiningSyntax is PropertyDeclarationSyntax propertyDeclaration + && propertyDeclaration.ExpressionBody is ArrowExpressionClauseSyntax arrowExpression) { - return new INamedTypeSymbol[][] { new INamedTypeSymbol[] { isSupportedType } }; + return DecomposeConditionForIsSupportedGroups(context, model, arrowExpression.Expression); } } + + return Array.Empty(); + } + + private static INamedTypeSymbol[][] DecomposeConditionForIsSupportedGroups(OperationAnalysisContext context, SemanticModel model, ExpressionSyntax expressionToDecompose) + { + if (expressionToDecompose is ParenthesizedExpressionSyntax parenthesizedExpression) + { + return DecomposeConditionForIsSupportedGroups(context, model, parenthesizedExpression.Expression); + } + if (expressionToDecompose is MemberAccessExpressionSyntax || expressionToDecompose is IdentifierNameSyntax) + { + return DecomposePropertySymbolForIsSupportedGroups_Property(context, model, expressionToDecompose); + } else if (expressionToDecompose is BinaryExpressionSyntax binaryExpression) { - var decomposedLeft = DecomposeConditionForIsSupportedGroups(model, binaryExpression.Left); - var decomposedRight = DecomposeConditionForIsSupportedGroups(model, binaryExpression.Right); + var decomposedLeft = DecomposeConditionForIsSupportedGroups(context, model, binaryExpression.Left); + var decomposedRight = DecomposeConditionForIsSupportedGroups(context, model, binaryExpression.Right); if (binaryExpression.OperatorToken is SyntaxToken operatorToken && operatorToken.ValueText == "&&") { + if (decomposedLeft.Length == 0) + return decomposedRight; + else if (decomposedRight.Length == 0) + return decomposedLeft; + + if ((decomposedLeft.Length > 1) || (decomposedRight.Length > 1)) + { + context.ReportDiagnostic(Diagnostic.Create(RuleCantParse, expressionToDecompose.GetLocation())); + } + return new INamedTypeSymbol[][] { GatherAndConditions(model, binaryExpression) }; } else if (binaryExpression.OperatorToken is SyntaxToken operatorToken2 && operatorToken2.ValueText == "||") { if (decomposedLeft.Length == 0 || decomposedRight.Length == 0) { + if (decomposedLeft.Length != 0 || decomposedRight.Length != 0) + { + context.ReportDiagnostic(Diagnostic.Create(RuleCantParse, expressionToDecompose.GetLocation())); + } return Array.Empty(); } var retVal = new INamedTypeSymbol[decomposedLeft.Length + decomposedRight.Length][]; @@ -280,11 +345,52 @@ private static INamedTypeSymbol[][] DecomposeConditionForIsSupportedGroups(Seman Array.Copy(decomposedRight, 0, retVal, decomposedLeft.Length, decomposedRight.Length); return retVal; } + else + { + if (decomposedLeft.Length != 0 || decomposedRight.Length != 0) + { + context.ReportDiagnostic(Diagnostic.Create(RuleCantParse, expressionToDecompose.GetLocation())); + } + } + } + else if (expressionToDecompose is PrefixUnaryExpressionSyntax prefixUnaryExpression) + { + var decomposedOperand = DecomposeConditionForIsSupportedGroups(context, model, prefixUnaryExpression.Operand); + + if (decomposedOperand.Length != 0) + context.ReportDiagnostic(Diagnostic.Create(RuleCantParse, expressionToDecompose.GetLocation())); + } + else if (expressionToDecompose is ConditionalExpressionSyntax conditionalExpressionSyntax) + { + var decomposedTrue = DecomposeConditionForIsSupportedGroups(context, model, conditionalExpressionSyntax.WhenTrue); + var decomposedFalse = DecomposeConditionForIsSupportedGroups(context, model, conditionalExpressionSyntax.WhenFalse); + if (decomposedTrue.Length != 0 || decomposedFalse.Length != 0) + { + context.ReportDiagnostic(Diagnostic.Create(RuleCantParse, expressionToDecompose.GetLocation())); + } } return Array.Empty(); } - private static bool ConditionAllowsSymbol(ISymbol symbolOfInvokeTarget, INamedTypeSymbol namedTypeThatIsSafeToUse) + private static IEnumerable GetBypassForIntrinsicHelperUseList(ISymbol symbol, IntrinsicsAnalyzerOnLoadData onLoadData) + { + var bypassReadyToRunForIntrinsicsHelperUse = onLoadData.BypassReadyToRunForIntrinsicsHelperUse; + if (bypassReadyToRunForIntrinsicsHelperUse != null) + { + foreach (var attributeData in symbol.GetAttributes()) + { + if (attributeData.AttributeClass.Equals(bypassReadyToRunForIntrinsicsHelperUse, SymbolEqualityComparer.Default)) + { + if (attributeData.ConstructorArguments[0].Value is INamedTypeSymbol attributeTypeSymbol) + { + yield return attributeTypeSymbol; + } + } + } + } + } + + private static bool ConditionAllowsSymbol(ISymbol symbolOfInvokeTarget, INamedTypeSymbol namedTypeThatIsSafeToUse, IntrinsicsAnalyzerOnLoadData onLoadData) { HashSet examinedSymbols = new HashSet(SymbolEqualityComparer.Default); Stack symbolsToExamine = new Stack(); @@ -296,6 +402,12 @@ private static bool ConditionAllowsSymbol(ISymbol symbolOfInvokeTarget, INamedTy if (symbolOfInvokeTarget.ContainingSymbol.Equals(symbol, SymbolEqualityComparer.Default)) return true; + foreach (var helperForType in GetBypassForIntrinsicHelperUseList(symbolOfInvokeTarget, onLoadData)) + { + if (helperForType.Equals(symbol, SymbolEqualityComparer.Default)) + return true; + } + examinedSymbols.Add(symbol); if (symbol.ContainingType != null && !examinedSymbols.Contains(symbol.ContainingType)) symbolsToExamine.Push(symbol.ContainingType); @@ -328,6 +440,18 @@ private static bool TypeSymbolAllowsTypeSymbol(INamedTypeSymbol namedTypeToCheck return false; } + private static INamespaceSymbol? SymbolToNamespaceSymbol(ISymbol symbol) + { + if (symbol.ContainingType != null) + return SymbolToNamespaceSymbol(symbol.ContainingType); + + if (symbol.ContainingNamespace != null) + { + return symbol.ContainingNamespace; + } + + return null; + } private static void AnalyzeOperation(IOperation operation, IMethodSymbol methodSymbol, OperationAnalysisContext context, IntrinsicsAnalyzerOnLoadData onLoadData) { var symbol = GetOperationSymbol(operation); @@ -337,31 +461,32 @@ private static void AnalyzeOperation(IOperation operation, IMethodSymbol methodS return; } - if (methodSymbol.ContainingType.Equals(symbol.ContainingSymbol, SymbolEqualityComparer.Default)) + bool methodNeedsProtectionWithIsSupported = false; +#pragma warning disable RS1024 + if (onLoadData.NamedTypesToBeProtected.Contains(symbol.ContainingSymbol)) + { + methodNeedsProtectionWithIsSupported = true; + } +#pragma warning restore RS1024 + + if (methodNeedsProtectionWithIsSupported && methodSymbol.ContainingType.Equals(symbol.ContainingSymbol, SymbolEqualityComparer.Default)) { return; // Intrinsic functions on their containing type can call themselves } - bool methodNeedsProtectionWithIsSupported = false; - foreach (var search in onLoadData.NamedTypesToBeProtected) + if (!methodNeedsProtectionWithIsSupported) { - if (search.Equals(symbol.ContainingSymbol, SymbolEqualityComparer.Default)) - { + if (GetBypassForIntrinsicHelperUseList(symbol, onLoadData).Any()) methodNeedsProtectionWithIsSupported = true; - } } if (!methodNeedsProtectionWithIsSupported) - return; - - if (symbol is IPropertySymbol propertySymbol) { - if (propertySymbol.Name == "IsSupported") - { - return; - } + return; } + var bypassReadyToRunForIntrinsicsHelperUse = onLoadData.BypassReadyToRunForIntrinsicsHelperUse; + ISymbol? symbolThatMightHaveIntrinsicsHelperAttribute = methodSymbol; IOperation operationSearch = operation; while (operationSearch != null) @@ -382,20 +507,52 @@ private static void AnalyzeOperation(IOperation operation, IMethodSymbol methodS operationSearch = operationSearch.Parent; } - var bypassReadyToRunForIntrinsicsHelperUse = onLoadData.BypassReadyToRunForIntrinsicsHelperUse; - if ((bypassReadyToRunForIntrinsicsHelperUse != null) && symbolThatMightHaveIntrinsicsHelperAttribute != null) + if (symbol is IPropertySymbol propertySymbol) { - foreach (var attributeData in symbolThatMightHaveIntrinsicsHelperAttribute.GetAttributes()) + if (propertySymbol.Name == "IsSupported") { - if (attributeData.AttributeClass.Equals(bypassReadyToRunForIntrinsicsHelperUse, SymbolEqualityComparer.Default)) + ISymbol? attributeExplicitlyAllowsRelatedSymbol = null; + if ((bypassReadyToRunForIntrinsicsHelperUse != null) && symbolThatMightHaveIntrinsicsHelperAttribute != null) { - if (attributeData.ConstructorArguments[0].Value is INamedTypeSymbol attributeTypeSymbol && ConditionAllowsSymbol(symbol, attributeTypeSymbol)) + foreach (var attributeData in symbolThatMightHaveIntrinsicsHelperAttribute.GetAttributes()) { - // This attribute indicates that this method will only be compiled into a ReadyToRun image if the behavior - // of the associated IsSupported method is defined to a constant value during ReadyToRun compilation that cannot change at runtime - return; + if (attributeData.AttributeClass.Equals(bypassReadyToRunForIntrinsicsHelperUse, SymbolEqualityComparer.Default)) + { + if (attributeData.ConstructorArguments[0].Value is INamedTypeSymbol attributeTypeSymbol) + { + var namespaceAttributeTypeSymbol = SymbolToNamespaceSymbol(attributeTypeSymbol); + var namespaceSymbol = SymbolToNamespaceSymbol(symbol); + if ((namespaceAttributeTypeSymbol != null) && (namespaceSymbol != null)) + { + if (!ConditionAllowsSymbol(symbol, attributeTypeSymbol, onLoadData) && namespaceAttributeTypeSymbol.Equals(namespaceSymbol, SymbolEqualityComparer.Default)) + { + attributeExplicitlyAllowsRelatedSymbol = attributeTypeSymbol; + } + } + } + } } } + + if (attributeExplicitlyAllowsRelatedSymbol != null) + { + context.ReportDiagnostic(Diagnostic.Create(RuleAttributeNotSpecificEnough, operation.Syntax.GetLocation(), attributeExplicitlyAllowsRelatedSymbol.ToDisplayString())); + } + + return; + } + } + + if (symbolThatMightHaveIntrinsicsHelperAttribute != null) + { + foreach (var attributeTypeSymbol in GetBypassForIntrinsicHelperUseList(symbolThatMightHaveIntrinsicsHelperAttribute, onLoadData)) + { + if (ConditionAllowsSymbol(symbol, attributeTypeSymbol, onLoadData)) + { + // This attribute indicates that this method will only be compiled into a ReadyToRun image if the behavior + // of the associated IsSupported method is defined to a constant value during ReadyToRun compilation that cannot change at runtime + return; + } } } @@ -433,85 +590,76 @@ bool HandleConditionalCase(ExpressionSyntax condition, SyntaxNode? syntaxOnPosit { if (previousNode == syntaxOnPositiveCondition) { - var decomposedCondition = DecomposeConditionForIsSupportedGroups(operation.SemanticModel, condition); - if (decomposedCondition.Length == 1) + var decomposedCondition = DecomposeConditionForIsSupportedGroups(context, operation.SemanticModel, condition); + + if (decomposedCondition.Length == 0) + return false; + + // Ensure every symbol found in the condition is only in 1 OR clause + HashSet foundSymbols = new HashSet(SymbolEqualityComparer.Default); + foreach (var andClause in decomposedCondition) { - foreach (var symbolFromCondition in decomposedCondition[0]) + foreach (var symbolInOrClause in andClause) { - if (ConditionAllowsSymbol(symbol, symbolFromCondition)) + if (!foundSymbols.Add(symbolInOrClause)) { - // There is a good IsSupported check with a positive check for the IsSupported call involved. Do not report. + context.ReportDiagnostic(Diagnostic.Create(RuleCantParse, operation.Syntax.GetLocation())); return true; } } } - else if (decomposedCondition.Length > 1) + + // Determine which sets of conditions have been excluded + List includedClauses = new List(); + for (int andClauseIndex = 0; andClauseIndex < decomposedCondition.Length; andClauseIndex++) { - // Ensure every symbol found in the condition is only in 1 OR clause - HashSet foundSymbols = new HashSet(SymbolEqualityComparer.Default); - foreach (var andClause in decomposedCondition) + bool foundMatchInAndClause = false; + foreach (var symbolInAndClause in decomposedCondition[andClauseIndex]) { - foreach (var symbolInOrClause in andClause) + foreach (var notType in notTypes) { - if (!foundSymbols.Add(symbolInOrClause)) + if (TypeSymbolAllowsTypeSymbol(notType, symbolInAndClause)) { - context.ReportDiagnostic(Diagnostic.Create(RuleCantParse, operation.Syntax.GetLocation())); - return true; + foundMatchInAndClause = true; + break; } } + if (foundMatchInAndClause) + break; } - // Check to see if all of the OR conditions other than 1 have been eliminated via if statements excluding those symbols - int indexNotExcluded = -1; - for (int andClauseIndex = 0; andClauseIndex < decomposedCondition.Length; andClauseIndex++) + if (!foundMatchInAndClause) { - bool foundMatchInAndClause = false; - foreach (var symbolInAndClause in decomposedCondition[andClauseIndex]) - { - foreach (var notType in notTypes) - { - if (TypeSymbolAllowsTypeSymbol(notType, symbolInAndClause)) - { - foundMatchInAndClause = true; - break; - } - } - if (foundMatchInAndClause) - break; - } - - if (!foundMatchInAndClause) - { - if (indexNotExcluded == -1) - { - indexNotExcluded = andClauseIndex; - } - else - { - // Multiple And clause groups not excluded. We didn't find a unique one. - indexNotExcluded = -1; - break; - } - } + includedClauses.Add(andClauseIndex); } + } - if (indexNotExcluded != -1) + // Each one of these clauses must be supported by the function being called + // or there is a lack of safety + + foreach (var clauseIndex in includedClauses) + { + bool clauseAllowsSymbol = false; + + var andClause = decomposedCondition[clauseIndex]; + foreach (var symbolFromCondition in andClause) { - var andClause = decomposedCondition[indexNotExcluded]; - foreach (var symbolFromCondition in andClause) + if (ConditionAllowsSymbol(symbol, symbolFromCondition, onLoadData)) { - if (ConditionAllowsSymbol(symbol, symbolFromCondition)) - { - // There is a good IsSupported check with a positive check for the IsSupported call involved. Do not report. - return true; - } + // There is a good IsSupported check with a positive check for the IsSupported call involved. Do not report. + clauseAllowsSymbol = true; } } + + if (!clauseAllowsSymbol) + return false; } + + return true; } else if (previousNode == syntaxOnNegativeCondition) { - var decomposedCondition = DecomposeConditionForIsSupportedGroups(operation.SemanticModel, condition); + var decomposedCondition = DecomposeConditionForIsSupportedGroups(context, operation.SemanticModel, condition); if (decomposedCondition.Length == 1) { foreach (var symbolFromCondition in decomposedCondition[0]) @@ -527,7 +675,10 @@ bool HandleConditionalCase(ExpressionSyntax condition, SyntaxNode? syntaxOnPosit previousNode = ancestorNode; } - context.ReportDiagnostic(Diagnostic.Create(Rule, operation.Syntax.GetLocation(), symbol.ContainingSymbol.ToDisplayString())); + if (onLoadData.NamedTypesToBeProtected.Contains(symbol.ContainingType)) + context.ReportDiagnostic(Diagnostic.Create(Rule, operation.Syntax.GetLocation(), symbol.ContainingSymbol.ToDisplayString())); + else + context.ReportDiagnostic(Diagnostic.Create(RuleHelper, operation.Syntax.GetLocation(), symbol.ToDisplayString())); } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs index ab5e2af084066b..09b0e1d40ec8bb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs @@ -480,6 +480,8 @@ private static unsafe void Avx2Decode(ref byte* srcBytes, ref byte* destBytes, b } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] private static Vector128 SimdShuffle(Vector128 left, Vector128 right, Vector128 mask8F) { Debug.Assert((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian); diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.cs index a42bca4c73dc07..dee2117bc146cd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.cs @@ -1381,6 +1381,8 @@ private unsafe bool TryFormatX(Span destination, out int charsWrit } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] private static (Vector128, Vector128, Vector128) FormatGuidVector128Utf8(Guid value, bool useDashes) { Debug.Assert((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian); diff --git a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAnyAsciiSearcher.cs b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAnyAsciiSearcher.cs index 93d6878ce1fe1b..10c60a45b2c7f3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAnyAsciiSearcher.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAnyAsciiSearcher.cs @@ -809,7 +809,7 @@ internal static int LastIndexOfAnyVectorized(ref byte searchSpace, int } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] private static Vector128 IndexOfAnyLookup(Vector128 source0, Vector128 source1, Vector128 bitmapLookup) @@ -1000,6 +1000,7 @@ private static unsafe int ComputeLastIndexOverlapped(ref T searchSp } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static unsafe int ComputeFirstIndex(ref T searchSpace, ref T current, Vector256 result) where TNegator : struct, INegator { @@ -1015,6 +1016,7 @@ private static unsafe int ComputeFirstIndex(ref T searchSpace, ref } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static unsafe int ComputeFirstIndexOverlapped(ref T searchSpace, ref T current0, ref T current1, Vector256 result) where TNegator : struct, INegator { @@ -1036,6 +1038,7 @@ private static unsafe int ComputeFirstIndexOverlapped(ref T searchS } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static unsafe int ComputeLastIndex(ref T searchSpace, ref T current, Vector256 result) where TNegator : struct, INegator { @@ -1051,6 +1054,7 @@ private static unsafe int ComputeLastIndex(ref T searchSpace, ref T } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static unsafe int ComputeLastIndexOverlapped(ref T searchSpace, ref T secondVector, Vector256 result) where TNegator : struct, INegator { diff --git a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/ProbabilisticMap.cs b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/ProbabilisticMap.cs index 340304b3224d24..b62e5f698aadcc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/ProbabilisticMap.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/ProbabilisticMap.cs @@ -356,6 +356,8 @@ internal static int LastIndexOfAny(ref uint charMap, ref char searchSp return -1; } + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse41))] private static int IndexOfAnyVectorized(ref uint charMap, ref char searchSpace, int searchSpaceLength, ReadOnlySpan values) { Debug.Assert(Sse41.IsSupported || AdvSimd.Arm64.IsSupported); @@ -367,7 +369,9 @@ private static int IndexOfAnyVectorized(ref uint charMap, ref char searchSpace, Vector128 charMapLower = Vector128.LoadUnsafe(ref Unsafe.As(ref charMap)); Vector128 charMapUpper = Vector128.LoadUnsafe(ref Unsafe.As(ref charMap), (nuint)Vector128.Count); +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // In this case, we have an else clause which has the same semantic meaning whether or not Avx2 is considered supported or unsupported if (Avx2.IsSupported && searchSpaceLength >= 32) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { Vector256 charMapLower256 = Vector256.Create(charMapLower, charMapLower); Vector256 charMapUpper256 = Vector256.Create(charMapUpper, charMapUpper); diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Equality.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Equality.cs index 4aa9f73ce3ecdb..ae8bd5f33469b2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Equality.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Equality.cs @@ -6,6 +6,7 @@ using System.Runtime.InteropServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.Arm; +using System.Runtime.Intrinsics.X86; using System.Text.Unicode; using System.Numerics; @@ -181,7 +182,7 @@ private static bool Equals(ReadOnlySpan left, ReadOnlySpan right) where } } } - else if (Vector256.IsHardwareAccelerated && right.Length >= Vector256.Count) + else if (Avx.IsSupported && right.Length >= Vector256.Count) { ref T currentLeftSearchSpace = ref MemoryMarshal.GetReference(left); ref T oneVectorAwayFromLeftEnd = ref Unsafe.Add(ref currentLeftSearchSpace, left.Length - Vector256.Count); diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.cs index 0b9af8cf0c6ae9..85801e101a1735 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; namespace System.Text { @@ -112,7 +113,7 @@ private static unsafe bool IsValidCore(ref T searchSpace, int length) where T Vector128.LoadUnsafe(ref Unsafe.Subtract(ref searchSpaceEnd, Vector128.Count))); } - if (Vector256.IsHardwareAccelerated) + if (Avx.IsSupported) { // Process inputs with lengths [33, 64] bytes. if (length <= 2 * Vector256.Count) diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Latin1Utility.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Latin1Utility.cs index 2450fa432c1a88..18b81c8d4b2c8e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Latin1Utility.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Latin1Utility.cs @@ -261,7 +261,9 @@ private static unsafe nuint GetIndexOfFirstNonLatin1Char_Sse2(char* pBuffer, nui secondVector = Sse2.LoadAlignedVector128((ushort*)pBuffer + SizeOfVector128InChars); Vector128 combinedVector = Sse2.Or(firstVector, secondVector); +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // In this case, we have an else clause which has the same semantic meaning whether or not Sse41 is considered supported or unsupported if (Sse41.IsSupported) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { // If a non-Latin-1 bit is set in any WORD of the combined vector, we have seen non-Latin-1 data. // Jump to the non-Latin-1 handler to figure out which particular vector contained non-Latin-1 data. @@ -304,7 +306,9 @@ private static unsafe nuint GetIndexOfFirstNonLatin1Char_Sse2(char* pBuffer, nui firstVector = Sse2.LoadAlignedVector128((ushort*)pBuffer); +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // In this case, we have an else clause which has the same semantic meaning whether or not Sse41 is considered supported or unsupported if (Sse41.IsSupported) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { // If a non-Latin-1 bit is set in any WORD of the combined vector, we have seen non-Latin-1 data. // Jump to the non-Latin-1 handler to figure out which particular vector contained non-Latin-1 data. @@ -337,7 +341,9 @@ private static unsafe nuint GetIndexOfFirstNonLatin1Char_Sse2(char* pBuffer, nui pBuffer = (char*)((byte*)pBuffer + (bufferLength & (SizeOfVector128InBytes - 1)) - SizeOfVector128InBytes); firstVector = Sse2.LoadVector128((ushort*)pBuffer); // unaligned load +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // In this case, we have an else clause which has the same semantic meaning whether or not Sse41 is considered supported or unsupported if (Sse41.IsSupported) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { // If a non-Latin-1 bit is set in any WORD of the combined vector, we have seen non-Latin-1 data. // Jump to the non-Latin-1 handler to figure out which particular vector contained non-Latin-1 data. @@ -371,7 +377,9 @@ private static unsafe nuint GetIndexOfFirstNonLatin1Char_Sse2(char* pBuffer, nui // we'll make sure the first vector local is the one that contains the non-Latin-1 data. // See comment earlier in the method for an explanation of how the below logic works. +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // In this case, we have an else clause which has the same semantic meaning whether or not Sse41 is considered supported or unsupported if (Sse41.IsSupported) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { if (!Sse41.TestZ(firstVector, latin1MaskForTestZ)) { @@ -446,7 +454,9 @@ private static unsafe nuint GetIndexOfFirstNonLatin1Char_Sse2(char* pBuffer, nui if ((bufferLength & 4) != 0) { +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // In this case, we have an else clause which has the same semantic meaning whether or not Bmi1.X64 is considered supported or unsupported if (Bmi1.X64.IsSupported) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { // If we can use 64-bit tzcnt to count the number of leading Latin-1 chars, prefer it. @@ -781,7 +791,9 @@ private static unsafe nuint NarrowUtf16ToLatin1_Sse2(char* pUtf16Buffer, byte* p // If there's non-Latin-1 data in the first 8 elements of the vector, there's nothing we can do. // See comments in GetIndexOfFirstNonLatin1Char_Sse2 for information about how this works. +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // In this case, we have an else clause which has the same semantic meaning whether or not Sse41 is considered supported or unsupported if (Sse41.IsSupported) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { if (!Sse41.TestZ(utf16VectorFirst, latin1MaskForTestZ)) { @@ -821,7 +833,9 @@ private static unsafe nuint NarrowUtf16ToLatin1_Sse2(char* pUtf16Buffer, byte* p utf16VectorFirst = Sse2.LoadVector128((short*)pUtf16Buffer + currentOffsetInElements); // unaligned load // See comments earlier in this method for information about how this works. +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // In this case, we have an else clause which has the same semantic meaning whether or not Sse41 is considered supported or unsupported if (Sse41.IsSupported) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { if (!Sse41.TestZ(utf16VectorFirst, latin1MaskForTestZ)) { @@ -860,7 +874,9 @@ private static unsafe nuint NarrowUtf16ToLatin1_Sse2(char* pUtf16Buffer, byte* p Vector128 combinedVector = Sse2.Or(utf16VectorFirst, utf16VectorSecond); // See comments in GetIndexOfFirstNonLatin1Char_Sse2 for information about how this works. +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // In this case, we have an else clause which has the same semantic meaning whether or not Sse41 is considered supported or unsupported if (Sse41.IsSupported) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { if (!Sse41.TestZ(combinedVector, latin1MaskForTestZ)) { @@ -894,7 +910,9 @@ private static unsafe nuint NarrowUtf16ToLatin1_Sse2(char* pUtf16Buffer, byte* p // Can we at least narrow the high vector? // See comments in GetIndexOfFirstNonLatin1Char_Sse2 for information about how this works. +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // In this case, we have an else clause which has the same semantic meaning whether or not Sse41 is considered supported or unsupported if (Sse41.IsSupported) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { if (!Sse41.TestZ(utf16VectorFirst, latin1MaskForTestZ)) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.Validation.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.Validation.cs index 6bf314dba8ca97..c24cdef18a8bb4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.Validation.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.Validation.cs @@ -130,7 +130,7 @@ internal static unsafe partial class Utf16Utility // bit for 1-byte or 2-byte elements. The 0x0080 bit will already have been set for non-ASCII (2-byte // and 3-byte) elements. - if (AdvSimd.IsSupported) + if (AdvSimd.Arm64.IsSupported) { charIsThreeByteUtf8Encoded = AdvSimd.AddSaturate(utf16Data, vector7800); mask = GetNonAsciiBytes(AdvSimd.Or(charIsNonAscii, charIsThreeByteUtf8Encoded).AsByte(), bitMask128); From cdc8b3f320fb984883ee4e99a3c0449b11a529ad Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 8 May 2023 17:44:12 -0700 Subject: [PATCH 07/19] Add pragmas and attributes to the packed span helpers - These use a peculiar construct but its legal as its all containing in 1 method. If it got more complex than that, the analysis would be quite a bit harder --- .../src/System/SpanHelpers.Packed.cs | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs index fe559da2db1d18..0c2f1b8ca987ec 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs @@ -35,37 +35,46 @@ public static unsafe bool CanUsePackedIndexOf(T value) } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] public static int IndexOf(ref char searchSpace, char value, int length) => IndexOf>(ref Unsafe.As(ref searchSpace), (short)value, length); [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] public static int IndexOfAnyExcept(ref char searchSpace, char value, int length) => IndexOf>(ref Unsafe.As(ref searchSpace), (short)value, length); [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] public static int IndexOfAny(ref char searchSpace, char value0, char value1, int length) => IndexOfAny>(ref Unsafe.As(ref searchSpace), (short)value0, (short)value1, length); [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] public static int IndexOfAnyExcept(ref char searchSpace, char value0, char value1, int length) => IndexOfAny>(ref Unsafe.As(ref searchSpace), (short)value0, (short)value1, length); [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] public static int IndexOfAny(ref char searchSpace, char value0, char value1, char value2, int length) => IndexOfAny>(ref Unsafe.As(ref searchSpace), (short)value0, (short)value1, (short)value2, length); [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] public static int IndexOfAnyExcept(ref char searchSpace, char value0, char value1, char value2, int length) => IndexOfAny>(ref Unsafe.As(ref searchSpace), (short)value0, (short)value1, (short)value2, length); [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] public static int IndexOfAnyInRange(ref char searchSpace, char lowInclusive, char rangeInclusive, int length) => IndexOfAnyInRange>(ref Unsafe.As(ref searchSpace), (short)lowInclusive, (short)rangeInclusive, length); [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] public static int IndexOfAnyExceptInRange(ref char searchSpace, char lowInclusive, char rangeInclusive, int length) => IndexOfAnyInRange>(ref Unsafe.As(ref searchSpace), (short)lowInclusive, (short)rangeInclusive, length); + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] public static bool Contains(ref short searchSpace, short value, int length) { Debug.Assert(CanUsePackedIndexOf(value)); @@ -105,7 +114,9 @@ public static bool Contains(ref short searchSpace, short value, int length) { ref short currentSearchSpace = ref searchSpace; +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else condition for this if statement is identical in semantics to Avx2 specific code if (Avx2.IsSupported && length > Vector256.Count) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { Vector256 packedValue = Vector256.Create((byte)value); @@ -158,7 +169,16 @@ public static bool Contains(ref short searchSpace, short value, int length) { Vector128 packedValue = Vector128.Create((byte)value); +#pragma warning disable IntrinsicsInSystemPrivateCoreLibConditionParsing // A negated IsSupported condition isn't parseable by the intrinsics analyzer, but in this case, it is only used in combination + // with the check above of Avx2.IsSupported && length > Vector256.Count which makes the logic + // in this if statement dead code when Avx2.IsSupported. Presumably this negated IsSupported check is to assist the JIT in + // not generating dead code. +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // This is paired with the check above, and since these if statements are contained in 1 function, the code + // may take a dependence on the JIT compiler producing a consistent value for the result of a call to IsSupported + // This logic MUST NOT be extracted to a helper function if (!Avx2.IsSupported && length > 2 * Vector128.Count) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough +#pragma warning restore IntrinsicsInSystemPrivateCoreLibConditionParsing { // Process the input in chunks of 16 characters (2 * Vector128). // If the input length is a multiple of 16, don't consume the last 16 characters in this loop. @@ -208,6 +228,7 @@ public static bool Contains(ref short searchSpace, short value, int length) return false; } + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] private static int IndexOf(ref short searchSpace, short value, int length) where TNegator : struct, SpanHelpers.INegator { @@ -242,7 +263,9 @@ private static int IndexOf(ref short searchSpace, short value, int len { ref short currentSearchSpace = ref searchSpace; +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else condition for this if statement is identical in semantics to Avx2 specific code if (Avx2.IsSupported && length > Vector256.Count) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { Vector256 packedValue = Vector256.Create((byte)value); @@ -297,7 +320,16 @@ private static int IndexOf(ref short searchSpace, short value, int len { Vector128 packedValue = Vector128.Create((byte)value); +#pragma warning disable IntrinsicsInSystemPrivateCoreLibConditionParsing // A negated IsSupported condition isn't parseable by the intrinsics analyzer, but in this case, it is only used in combination + // with the check above of Avx2.IsSupported && length > Vector256.Count which makes the logic + // in this if statement dead code when Avx2.IsSupported. Presumably this negated IsSupported check is to assist the JIT in + // not generating dead code. +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // This is paired with the check above, and since these if statements are contained in 1 function, the code + // may take a dependence on the JIT compiler producing a consistent value for the result of a call to IsSupported + // This logic MUST NOT be extracted to a helper function if (!Avx2.IsSupported && length > 2 * Vector128.Count) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough +#pragma warning restore IntrinsicsInSystemPrivateCoreLibConditionParsing { // Process the input in chunks of 16 characters (2 * Vector128). // If the input length is a multiple of 16, don't consume the last 16 characters in this loop. @@ -349,6 +381,7 @@ private static int IndexOf(ref short searchSpace, short value, int len return -1; } + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] private static int IndexOfAny(ref short searchSpace, short value0, short value1, int length) where TNegator : struct, SpanHelpers.INegator { @@ -390,7 +423,9 @@ private static int IndexOfAny(ref short searchSpace, short value0, sho { ref short currentSearchSpace = ref searchSpace; +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else condition for this if statement is identical in semantics to Avx2 specific code if (Avx2.IsSupported && length > Vector256.Count) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { Vector256 packedValue0 = Vector256.Create((byte)value0); Vector256 packedValue1 = Vector256.Create((byte)value1); @@ -447,7 +482,16 @@ private static int IndexOfAny(ref short searchSpace, short value0, sho Vector128 packedValue0 = Vector128.Create((byte)value0); Vector128 packedValue1 = Vector128.Create((byte)value1); +#pragma warning disable IntrinsicsInSystemPrivateCoreLibConditionParsing // A negated IsSupported condition isn't parseable by the intrinsics analyzer, but in this case, it is only used in combination + // with the check above of Avx2.IsSupported && length > Vector256.Count which makes the logic + // in this if statement dead code when Avx2.IsSupported. Presumably this negated IsSupported check is to assist the JIT in + // not generating dead code. +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // This is paired with the check above, and since these if statements are contained in 1 function, the code + // may take a dependence on the JIT compiler producing a consistent value for the result of a call to IsSupported + // This logic MUST NOT be extracted to a helper function if (!Avx2.IsSupported && length > 2 * Vector128.Count) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough +#pragma warning restore IntrinsicsInSystemPrivateCoreLibConditionParsing { // Process the input in chunks of 16 characters (2 * Vector128). // If the input length is a multiple of 16, don't consume the last 16 characters in this loop. @@ -499,6 +543,7 @@ private static int IndexOfAny(ref short searchSpace, short value0, sho return -1; } + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] private static int IndexOfAny(ref short searchSpace, short value0, short value1, short value2, int length) where TNegator : struct, SpanHelpers.INegator { @@ -541,7 +586,9 @@ private static int IndexOfAny(ref short searchSpace, short value0, sho { ref short currentSearchSpace = ref searchSpace; +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else condition for this if statement is identical in semantics to Avx2 specific code if (Avx2.IsSupported && length > Vector256.Count) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { Vector256 packedValue0 = Vector256.Create((byte)value0); Vector256 packedValue1 = Vector256.Create((byte)value1); @@ -600,7 +647,16 @@ private static int IndexOfAny(ref short searchSpace, short value0, sho Vector128 packedValue1 = Vector128.Create((byte)value1); Vector128 packedValue2 = Vector128.Create((byte)value2); +#pragma warning disable IntrinsicsInSystemPrivateCoreLibConditionParsing // A negated IsSupported condition isn't parseable by the intrinsics analyzer, but in this case, it is only used in combination + // with the check above of Avx2.IsSupported && length > Vector256.Count which makes the logic + // in this if statement dead code when Avx2.IsSupported. Presumably this negated IsSupported check is to assist the JIT in + // not generating dead code. +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // This is paired with the check above, and since these if statements are contained in 1 function, the code + // may take a dependence on the JIT compiler producing a consistent value for the result of a call to IsSupported + // This logic MUST NOT be extracted to a helper function if (!Avx2.IsSupported && length > 2 * Vector128.Count) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough +#pragma warning restore IntrinsicsInSystemPrivateCoreLibConditionParsing { // Process the input in chunks of 16 characters (2 * Vector128). // If the input length is a multiple of 16, don't consume the last 16 characters in this loop. @@ -652,6 +708,7 @@ private static int IndexOfAny(ref short searchSpace, short value0, sho return -1; } + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] private static int IndexOfAnyInRange(ref short searchSpace, short lowInclusive, short rangeInclusive, int length) where TNegator : struct, SpanHelpers.INegator { @@ -676,7 +733,9 @@ private static int IndexOfAnyInRange(ref short searchSpace, short lowI { ref short currentSearchSpace = ref searchSpace; +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else condition for this if statement is identical in semantics to Avx2 specific code if (Avx2.IsSupported && length > Vector256.Count) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { Vector256 lowVector = Vector256.Create((byte)lowInclusive); Vector256 rangeVector = Vector256.Create((byte)rangeInclusive); @@ -733,7 +792,16 @@ private static int IndexOfAnyInRange(ref short searchSpace, short lowI Vector128 lowVector = Vector128.Create((byte)lowInclusive); Vector128 rangeVector = Vector128.Create((byte)rangeInclusive); +#pragma warning disable IntrinsicsInSystemPrivateCoreLibConditionParsing // A negated IsSupported condition isn't parseable by the intrinsics analyzer, but in this case, it is only used in combination + // with the check above of Avx2.IsSupported && length > Vector256.Count which makes the logic + // in this if statement dead code when Avx2.IsSupported. Presumably this negated IsSupported check is to assist the JIT in + // not generating dead code. +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // This is paired with the check above, and since these if statements are contained in 1 function, the code + // may take a dependence on the JIT compiler producing a consistent value for the result of a call to IsSupported + // This logic MUST NOT be extracted to a helper function if (!Avx2.IsSupported && length > 2 * Vector128.Count) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough +#pragma warning restore IntrinsicsInSystemPrivateCoreLibConditionParsing { // Process the input in chunks of 16 characters (2 * Vector128). // If the input length is a multiple of 16, don't consume the last 16 characters in this loop. @@ -828,6 +896,7 @@ private static int ComputeFirstIndex(ref short searchSpace, ref short current, V } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static int ComputeFirstIndex(ref short searchSpace, ref short current, Vector256 equals) { uint notEqualsElements = FixUpPackedVector256Result(equals).ExtractMostSignificantBits(); @@ -850,6 +919,7 @@ private static int ComputeFirstIndexOverlapped(ref short searchSpace, ref short } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] private static int ComputeFirstIndexOverlapped(ref short searchSpace, ref short current0, ref short current1, Vector256 equals) { uint notEqualsElements = FixUpPackedVector256Result(equals).ExtractMostSignificantBits(); From a01fd9de20eac70645372f95bb6448d8768bbae1 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 9 May 2023 10:16:08 -0700 Subject: [PATCH 08/19] Hah! It works - Handle intrinsics calls from nested intrinsics types - Handle IsSupported checks in the presence of an attribute which explicitly allows an IsSupported check as well as one which doesn't, but is related - Add suppressions for Avx2 IsSupported checks in IndexOfAnyAsciiSearcher helper functions --- ...ntrinsicsInSystemPrivateCoreLibAnalyzer.cs | 19 +++++++--- .../IndexOfAnyAsciiSearcher.cs | 36 +++++++++++++++++++ .../IndexOfAnyValues/ProbabilisticMap.cs | 6 ++++ .../System/Runtime/Intrinsics/Vector128.cs | 6 ++-- 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs b/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs index befa51d9101234..68fdfeedac4b5f 100644 --- a/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs +++ b/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs @@ -469,7 +469,10 @@ private static void AnalyzeOperation(IOperation operation, IMethodSymbol methodS } #pragma warning restore RS1024 - if (methodNeedsProtectionWithIsSupported && methodSymbol.ContainingType.Equals(symbol.ContainingSymbol, SymbolEqualityComparer.Default)) + // A method on an intrinsic type can call other methods on the intrinsic type safely, as well as methods on the type that contains the method + if (methodNeedsProtectionWithIsSupported && + (methodSymbol.ContainingType.Equals(symbol.ContainingSymbol, SymbolEqualityComparer.Default) + || (methodSymbol.ContainingType.ContainingType != null && methodSymbol.ContainingType.ContainingType.Equals(symbol.ContainingType, SymbolEqualityComparer.Default)))) { return; // Intrinsic functions on their containing type can call themselves } @@ -512,6 +515,7 @@ private static void AnalyzeOperation(IOperation operation, IMethodSymbol methodS if (propertySymbol.Name == "IsSupported") { ISymbol? attributeExplicitlyAllowsRelatedSymbol = null; + ISymbol? attributeExplicitlyAllowsExactSymbol = null; if ((bypassReadyToRunForIntrinsicsHelperUse != null) && symbolThatMightHaveIntrinsicsHelperAttribute != null) { foreach (var attributeData in symbolThatMightHaveIntrinsicsHelperAttribute.GetAttributes()) @@ -524,9 +528,16 @@ private static void AnalyzeOperation(IOperation operation, IMethodSymbol methodS var namespaceSymbol = SymbolToNamespaceSymbol(symbol); if ((namespaceAttributeTypeSymbol != null) && (namespaceSymbol != null)) { - if (!ConditionAllowsSymbol(symbol, attributeTypeSymbol, onLoadData) && namespaceAttributeTypeSymbol.Equals(namespaceSymbol, SymbolEqualityComparer.Default)) + if (namespaceAttributeTypeSymbol.Equals(namespaceSymbol, SymbolEqualityComparer.Default)) { - attributeExplicitlyAllowsRelatedSymbol = attributeTypeSymbol; + if (ConditionAllowsSymbol(symbol, attributeTypeSymbol, onLoadData)) + { + attributeExplicitlyAllowsExactSymbol = attributeTypeSymbol; + } + else + { + attributeExplicitlyAllowsRelatedSymbol = attributeTypeSymbol; + } } } } @@ -534,7 +545,7 @@ private static void AnalyzeOperation(IOperation operation, IMethodSymbol methodS } } - if (attributeExplicitlyAllowsRelatedSymbol != null) + if ((attributeExplicitlyAllowsRelatedSymbol != null) && (attributeExplicitlyAllowsExactSymbol == null)) { context.ReportDiagnostic(Diagnostic.Create(RuleAttributeNotSpecificEnough, operation.Syntax.GetLocation(), attributeExplicitlyAllowsRelatedSymbol.ToDisplayString())); } diff --git a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAnyAsciiSearcher.cs b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAnyAsciiSearcher.cs index 10c60a45b2c7f3..7375606b68594c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAnyAsciiSearcher.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAnyAsciiSearcher.cs @@ -166,6 +166,9 @@ private static unsafe bool TryLastIndexOfAny(ref short searchSpace, in return false; } + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] internal static int IndexOfAnyVectorized(ref short searchSpace, int searchSpaceLength, Vector128 bitmap) where TNegator : struct, INegator where TOptimizations : struct, IOptimizations @@ -174,7 +177,9 @@ internal static int IndexOfAnyVectorized(ref short sea if (searchSpaceLength > 2 * Vector128.Count) { +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else clause is semantically equivalent if (Avx2.IsSupported) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { Vector256 bitmap256 = Vector256.Create(bitmap, bitmap); @@ -276,6 +281,9 @@ internal static int IndexOfAnyVectorized(ref short sea return -1; } + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] internal static int LastIndexOfAnyVectorized(ref short searchSpace, int searchSpaceLength, Vector128 bitmap) where TNegator : struct, INegator where TOptimizations : struct, IOptimizations @@ -284,7 +292,9 @@ internal static int LastIndexOfAnyVectorized(ref short if (searchSpaceLength > 2 * Vector128.Count) { +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else clause is semantically equivalent if (Avx2.IsSupported) +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { Vector256 bitmap256 = Vector256.Create(bitmap, bitmap); @@ -386,6 +396,9 @@ internal static int LastIndexOfAnyVectorized(ref short return -1; } + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] internal static int IndexOfAnyVectorized(ref byte searchSpace, int searchSpaceLength, Vector128 bitmap) where TNegator : struct, INegator { @@ -393,7 +406,9 @@ internal static int IndexOfAnyVectorized(ref byte searchSpace, int sea if (searchSpaceLength > Vector128.Count) { +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else clause is semantically equivalent if (Avx2.IsSupported) +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { Vector256 bitmap256 = Vector256.Create(bitmap, bitmap); @@ -491,6 +506,9 @@ internal static int IndexOfAnyVectorized(ref byte searchSpace, int sea return -1; } + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] internal static int LastIndexOfAnyVectorized(ref byte searchSpace, int searchSpaceLength, Vector128 bitmap) where TNegator : struct, INegator { @@ -498,7 +516,9 @@ internal static int LastIndexOfAnyVectorized(ref byte searchSpace, int if (searchSpaceLength > Vector128.Count) { +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else clause is semantically equivalent if (Avx2.IsSupported) +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { Vector256 bitmap256 = Vector256.Create(bitmap, bitmap); @@ -596,6 +616,9 @@ internal static int LastIndexOfAnyVectorized(ref byte searchSpace, int return -1; } + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] internal static int IndexOfAnyVectorized(ref byte searchSpace, int searchSpaceLength, Vector128 bitmap0, Vector128 bitmap1) where TNegator : struct, INegator { @@ -603,7 +626,9 @@ internal static int IndexOfAnyVectorized(ref byte searchSpace, int sea if (searchSpaceLength > Vector128.Count) { +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else clause is semantically equivalent if (Avx2.IsSupported) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { Vector256 bitmap256_0 = Vector256.Create(bitmap0, bitmap0); Vector256 bitmap256_1 = Vector256.Create(bitmap1, bitmap1); @@ -702,6 +727,9 @@ internal static int IndexOfAnyVectorized(ref byte searchSpace, int sea return -1; } + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] internal static int LastIndexOfAnyVectorized(ref byte searchSpace, int searchSpaceLength, Vector128 bitmap0, Vector128 bitmap1) where TNegator : struct, INegator { @@ -709,7 +737,9 @@ internal static int LastIndexOfAnyVectorized(ref byte searchSpace, int if (searchSpaceLength > Vector128.Count) { +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else clause is semantically equivalent if (Avx2.IsSupported) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { Vector256 bitmap256_0 = Vector256.Create(bitmap0, bitmap0); Vector256 bitmap256_1 = Vector256.Create(bitmap1, bitmap1); @@ -847,6 +877,9 @@ private static Vector128 IndexOfAnyLookup(Vector } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] private static Vector128 IndexOfAnyLookupCore(Vector128 source, Vector128 bitmapLookup) { // On X86, the Ssse3.Shuffle instruction will already perform an implicit 'AND 0xF' on the indices, so we can skip it. @@ -907,6 +940,9 @@ private static Vector256 IndexOfAnyLookupCore(Vector256 source, Vect } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] + [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] private static Vector128 IndexOfAnyLookup(Vector128 source, Vector128 bitmapLookup0, Vector128 bitmapLookup1) where TNegator : struct, INegator { diff --git a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/ProbabilisticMap.cs b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/ProbabilisticMap.cs index b62e5f698aadcc..942c6c5d4aedbb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/ProbabilisticMap.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/ProbabilisticMap.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.Arm; +using System.Runtime.Intrinsics.Wasm; using System.Runtime.Intrinsics.X86; #pragma warning disable IDE0060 // https://github.com/dotnet/roslyn-analyzers/issues/6228 @@ -167,6 +168,11 @@ private static Vector128 ContainsMask16Chars(Vector128 charMapLower, } [MethodImpl(MethodImplOptions.AggressiveInlining)] + [BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] + [BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] + [BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] + [BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] + [BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] private static Vector128 IsCharBitSet(Vector128 charMapLower, Vector128 charMapUpper, Vector128 values) { // X86 doesn't have a logical right shift intrinsic for bytes: https://github.com/dotnet/runtime/issues/82564 diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs index 90a78e6d646432..d6aa0266b9642e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs @@ -2444,8 +2444,10 @@ public static Vector128 Shuffle(Vector128 vector, Vector128 /// On hardware with support, indices are treated as modulo 16, and if the high bit is set, the result will be set to 0 for that element. /// On hardware with or support, this method behaves the same as Shuffle. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] // Because this method has specific behavior when this is used on a machine with SSSE3, mark with this attribute which will - // make the behavior of the function at runtime not be affected by R2R compilation behavior + [BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] + [BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] + [BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] + [BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] internal static Vector128 ShuffleUnsafe(Vector128 vector, Vector128 indices) { if (Ssse3.IsSupported) From 55737388299d07d1cb0989e2e56d8811803f935c Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 9 May 2023 10:51:23 -0700 Subject: [PATCH 09/19] Rename attribute and fix docs --- .../coreclr/botr/vectors-and-intrinsics.md | 60 ++++++++++++++-- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 2 +- .../Common/src/System/HexConverter.cs | 12 ++-- ...ntrinsicsInSystemPrivateCoreLibAnalyzer.cs | 30 ++++---- .../System.Private.CoreLib.Shared.projitems | 2 +- .../src/System/Buffers/Text/Base64Decoder.cs | 10 +-- .../src/System/Buffers/Text/Base64Encoder.cs | 6 +- .../System.Private.CoreLib/src/System/Guid.cs | 4 +- .../IndexOfAnyAsciiSearcher.cs | 70 +++++++++---------- .../IndexOfAnyValues/ProbabilisticMap.cs | 22 +++--- .../src/System/Numerics/Matrix4x4.Impl.cs | 2 +- .../CompExactlyDependsOn.cs} | 6 +- .../CompExactlyDependsOnAttribute.cs | 18 +++++ .../System/Runtime/Intrinsics/Vector128.cs | 24 +++---- .../src/System/SpanHelpers.Packed.cs | 36 +++++----- .../src/System/Text/Ascii.Utility.cs | 4 +- .../src/System/Text/Latin1Utility.cs | 6 +- .../Text/Unicode/Utf16Utility.Validation.cs | 2 +- .../Text/Unicode/Utf8Utility.Validation.cs | 2 +- 19 files changed, 190 insertions(+), 128 deletions(-) rename src/libraries/System.Private.CoreLib/src/System/Runtime/{BypassReadyToRunForIntrinsicsHelperUse.cs => CompilerServices/CompExactlyDependsOn.cs} (74%) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CompExactlyDependsOnAttribute.cs diff --git a/docs/design/coreclr/botr/vectors-and-intrinsics.md b/docs/design/coreclr/botr/vectors-and-intrinsics.md index dd6b9918da4636..93d280f338cdf0 100644 --- a/docs/design/coreclr/botr/vectors-and-intrinsics.md +++ b/docs/design/coreclr/botr/vectors-and-intrinsics.md @@ -61,14 +61,14 @@ Code will be compiled using the optimistic instruction set to drive compilation, - If an application developer is highly concerned about startup performance, developers should avoid use intrinsics beyond Sse42, or should use Crossgen with an updated baseline instruction set support. ### Crossgen2 adjustment to rules for System.Private.CoreLib.dll -Since System.Private.CoreLib.dll is known to be code reviewed with the code review rules as written above for crossgen1 with System.Private.CoreLib.dll, it is possible to relax rule "Code which attempts to use instruction sets outside of the optimistic set will generate code that will not be used on machines with support for the instruction set." What this will do is allow the generation of non-optimal code for these situations, but through the magic of code review and analyzers, the generated logic will still work correctly. +Since System.Private.CoreLib.dll is known to be code reviewed with the code review rules as written below with System.Private.CoreLib.dll, it is possible to relax rule "Code which attempts to use instruction sets outside of the optimistic set will generate code that will not be used on machines with support for the instruction set." What this will do is allow the generation of non-optimal code for these situations, but through the magic of code review and analyzers, the generated logic will still work correctly. -#### Code review rules for code written in System.Private.CoreLib.dll -- Any use of a platform intrinsic in the codebase MUST be wrapped with a call to an associated IsSupported property. This wrapping MUST be done within the same function that uses the hardware intrinsic, OR the function which uses the platform intrinsic must have the `BypassReadyToRunForIntrinsicsHelperUse` attribute used to indicate that this function will unconditionally call platform intrinsics of from some type. -- Within a single function that uses platform intrinsics, unless marked with the `BypassReadyToRunForIntrinsicsHelperUse` attribute it must behave identically regardless of whether IsSupported returns true or not. This allows the R2R compiler to compile with a lower set of intrinsics support, and yet expect that the behavior of the function will remain unchanged in the presence of tiered compilation. -- Excessive use of intrinsics may cause startup performance problems due to additional jitting, or may not achieve desired performance characteristics due to suboptimal codegen. To fix this, we may, in the future, change the compilation rules to compile the methods marked with`BypassReadyToRunForIntrinsicsHelperUse` with the appropriate platform intrinsics enabled. +#### Code review and analyzer rules for code written in System.Private.CoreLib.dll +- Any use of a platform intrinsic in the codebase MUST be wrapped with a call to an associated IsSupported property. This wrapping MUST be done within the same function that uses the hardware intrinsic, OR the function which uses the platform intrinsic must have the `CompExactlyDependsOn` attribute used to indicate that this function will unconditionally call platform intrinsics of from some type. +- Within a single function that uses platform intrinsics, unless marked with the `CompExactlyDependsOn` attribute it must behave identically regardless of whether IsSupported returns true or not. This allows the R2R compiler to compile with a lower set of intrinsics support, and yet expect that the behavior of the function will remain unchanged in the presence of tiered compilation. +- Excessive use of intrinsics may cause startup performance problems due to additional jitting, or may not achieve desired performance characteristics due to suboptimal codegen. To fix this, we may, in the future, change the compilation rules to compile the methods marked with`CompExactlyDependsOn` with the appropriate platform intrinsics enabled. -Correct use of the `IsSupported` properties and `BypassReadyToRunForIntrinsicsHelperUse` attribute is checked by an analyzer during build of `System.Private.CoreLib`. This analyzer requires that all usage of `IsSupported` properties conform to a few specific patterns. These patterns are supported via either if statements or the ternary operator. +Correct use of the `IsSupported` properties and `CompExactlyDependsOn` attribute is checked by an analyzer during build of `System.Private.CoreLib`. This analyzer requires that all usage of `IsSupported` properties conform to a few specific patterns. These patterns are supported via either if statements or the ternary operator. The supported conditional checks are @@ -109,7 +109,53 @@ if (Avx2.IsSupported || ArmBase.IsSupported) } ``` -The behavior of the `BypassReadyToRunForIntrinsicsHelperUse` is that 1 or more attributes may be applied to a given method. If any of the types specified via the attribute will not have an invariant result for its associated `IsSupported` property at runtime, then the method will not be compiled or inlined into another function during R2R compilation. If no type so described will have a true result for the `IsSupported` method, then the method will not be compiled or inlined into another function during R2R compilation. +4. Within a method marked with `CompExactlyDependsOn` for a less advanced attribute, there may be a use of an explicit IsSupported check for a more advanced cpu feature. If so, the behavior of the overall function must remain the same regardless of whether or not the CPU feature is enabled. The analyzer will detect this usage as a warning, so that any use of IsSupported in a helper method is examined to verify that that use follows the rule of preserving exactly equivalent behavior. + +``` +[CompExactlyDependsOn(typeof(Sse41))] +int DoSomethingHelper() +{ +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else clause is semantically equivalent + if (Avx2.IsSupported) +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough + { + Avx2.IntrinsicThatDoesTheSameThingAsSse41IntrinsicAndSse41.Intrinsic2(); + } + else + { + Sse41.Intrinsic(); + Sse41.Intrinsic2(); + } +} +``` + +- NOTE: If the helper needs to be used AND behave differently with different instruction sets enabled, correct logic requires spreading the `CompExactlyDependsOn` attribute to all callers such that no caller could be compiled expecting the wrong behavior. See the `Vector128.ShuffleUnsafe` method, and various uses. + + +The behavior of the `CompExactlyDependsOn` is that 1 or more attributes may be applied to a given method. If any of the types specified via the attribute will not have an invariant result for its associated `IsSupported` property at runtime, then the method will not be compiled or inlined into another function during R2R compilation. If no type so described will have a true result for the `IsSupported` method, then the method will not be compiled or inlined into another function during R2R compilation. + +5. In addition to directly using the IsSupported properties to enable/disable support for intrinsics, simple static properties written in the following style may be used to reduce code duplication. + +``` +static bool IsVectorizationSupported => Avx2.IsSupported || PackedSimd.IsSupported + +public void SomePublicApi() +{ + if (IsVectorizationSupported) + SomeVectorizationHelper(); + else + { + // Non-Vectorized implementation + } +} + +[CompExactlyDependsOn(typeof(Avx2))] +[CompExactlyDependsOn(typeof(PackedSimd))] +private void SomeVectorizationHelper() +{ +} +``` + # Mechanisms in the JIT to generate correct code to handle varied instruction set support The JIT receives flags which instruct it on what instruction sets are valid to use, and has access to a new jit interface api `notifyInstructionSetUsage(isa, bool supportBehaviorRequired)`. diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 2f2259a30dbfb2..511a52692eeb97 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -551,7 +551,7 @@ public static bool ShouldCodeNotBeCompiledIntoFinalImage(InstructionSetSupport i { return true; } - if (metadataReader.StringComparer.Equals(nameHandle, "BypassReadyToRunForIntrinsicsHelperUseAttribute")) + if (metadataReader.StringComparer.Equals(nameHandle, "CompExactlyDependsOnAttribute")) { var customAttribute = metadataReader.GetCustomAttribute(attributeHandle); var typeProvider = new CustomAttributeTypeProvider(ecmaMethod.Module); diff --git a/src/libraries/Common/src/System/HexConverter.cs b/src/libraries/Common/src/System/HexConverter.cs index 6e4571d7403989..ccce1cb691f104 100644 --- a/src/libraries/Common/src/System/HexConverter.cs +++ b/src/libraries/Common/src/System/HexConverter.cs @@ -91,8 +91,8 @@ public static void ToCharsBuffer(byte value, Span buffer, int startingInde #if SYSTEM_PRIVATE_CORELIB // Converts Vector128 into 2xVector128 ASCII Hex representation [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] internal static (Vector128, Vector128) AsciiToHexVector128(Vector128 src, Vector128 hexMap) { Debug.Assert(Ssse3.IsSupported || AdvSimd.Arm64.IsSupported); @@ -107,8 +107,8 @@ internal static (Vector128, Vector128) AsciiToHexVector128(Vector128 Vector128.ShuffleUnsafe(hexMap, highNibbles & Vector128.Create((byte)0xF))); } - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] private static void EncodeToUtf16_Vector128(ReadOnlySpan bytes, Span chars, Casing casing) { Debug.Assert(bytes.Length >= Vector128.Count); @@ -240,8 +240,8 @@ public static bool TryDecodeFromUtf16(ReadOnlySpan chars, Span bytes } #if SYSTEM_PRIVATE_CORELIB - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] + [CompExactlyDependsOn(typeof(Ssse3))] public static bool TryDecodeFromUtf16_Vector128(ReadOnlySpan chars, Span bytes) { Debug.Assert(Ssse3.IsSupported || AdvSimd.Arm64.IsSupported); diff --git a/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs b/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs index 68fdfeedac4b5f..dfb1bf599b4722 100644 --- a/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs +++ b/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs @@ -26,17 +26,15 @@ public class IntrinsicsInSystemPrivateCoreLibAnalyzer : DiagnosticAnalyzer { public const string DiagnosticId = "IntrinsicsInSystemPrivateCoreLib"; - // You can change these strings in the Resources.resx file. If you do not want your analyzer to be localize-able, you can use regular strings for Title and MessageFormat. - // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/Localizing%20Analyzers.md for more on localization private const string Title = "System.Private.CoreLib ReadyToRun Intrinsics"; - private const string MessageFormat = "Intrinsics from class '{0}' used without the protection of an explicit if statement checking the correct IsSupported flag or BypassReadyToRunForIntrinsicsHelperUse"; + private const string MessageFormat = "Intrinsics from class '{0}' used without the protection of an explicit if statement checking the correct IsSupported flag or CompExactlyDependsOn"; private const string Description = "ReadyToRun Intrinsic Safety For System.Private.CoreLib."; private const string Category = "IntrinsicsCorrectness"; private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description); public const string DiagnosticIdHelper = "IntrinsicsInSystemPrivateCoreLibHelper"; - private const string MessageHelperFormat = "Helper '{0}' used without the protection of an explicit if statement checking the correct IsSupported flag or BypassReadyToRunForIntrinsicsHelperUse"; + private const string MessageHelperFormat = "Helper '{0}' used without the protection of an explicit if statement checking the correct IsSupported flag or CompExactlyDependsOn"; private static readonly DiagnosticDescriptor RuleHelper = new DiagnosticDescriptor(DiagnosticIdHelper, Title, MessageHelperFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description); public const string DiagnosticIdConditionParsing = "IntrinsicsInSystemPrivateCoreLibConditionParsing"; @@ -44,7 +42,7 @@ public class IntrinsicsInSystemPrivateCoreLibAnalyzer : DiagnosticAnalyzer private static readonly DiagnosticDescriptor RuleCantParse = new DiagnosticDescriptor(DiagnosticIdConditionParsing, Title, MessageNonParseableConditionFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description); public const string DiagnosticIdAttributeNotSpecificEnough = "IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough"; - private const string MessageAttributeNotSpecificEnoughFormat = "BypassReadyToRunForIntrinsicsHelperUse({0}) attribute found which relates to this IsSupported check, but is not specific enough. Suppress this error if this function has an appropriate if condition so that if the meaning of the function is invariant regardless of the result of the call to IsSupported."; + private const string MessageAttributeNotSpecificEnoughFormat = "CompExactlyDependsOn({0}) attribute found which relates to this IsSupported check, but is not specific enough. Suppress this error if this function has an appropriate if condition so that if the meaning of the function is invariant regardless of the result of the call to IsSupported."; private static readonly DiagnosticDescriptor RuleAttributeNotSpecificEnough = new DiagnosticDescriptor(DiagnosticIdAttributeNotSpecificEnough, Title, MessageAttributeNotSpecificEnoughFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description); public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(Rule, RuleHelper, RuleCantParse, RuleAttributeNotSpecificEnough); } } @@ -121,15 +119,15 @@ private sealed class IntrinsicsAnalyzerOnLoadData { public IntrinsicsAnalyzerOnLoadData(HashSet namedTypesToBeProtected, INamedTypeSymbol? bypassReadyToRunAttribute, - INamedTypeSymbol? bypassReadyToRunForIntrinsicsHelperUse) + INamedTypeSymbol? compExaclyDependsOn) { NamedTypesToBeProtected = namedTypesToBeProtected; BypassReadyToRunAttribute = bypassReadyToRunAttribute; - BypassReadyToRunForIntrinsicsHelperUse = bypassReadyToRunForIntrinsicsHelperUse; + CompExactlyDependsOn = compExaclyDependsOn; } public readonly HashSet NamedTypesToBeProtected; public readonly INamedTypeSymbol? BypassReadyToRunAttribute; - public readonly INamedTypeSymbol? BypassReadyToRunForIntrinsicsHelperUse; + public readonly INamedTypeSymbol? CompExactlyDependsOn; } public override void Initialize(AnalysisContext context) @@ -141,9 +139,9 @@ public override void Initialize(AnalysisContext context) HashSet namedTypesToBeProtected = new HashSet(SymbolEqualityComparer.Default); INamespaceSymbol systemRuntimeIntrinsicsNamespace = GetNamespace(context.Compilation.Assembly, "System", "Runtime", "Intrinsics"); INamedTypeSymbol? bypassReadyToRunAttribute = context.Compilation.Assembly.GetTypeByMetadataName("System.Runtime.BypassReadyToRunAttribute"); - INamedTypeSymbol? bypassReadyToRunForIntrinsicsHelperUse = context.Compilation.Assembly.GetTypeByMetadataName("System.Runtime.BypassReadyToRunForIntrinsicsHelperUseAttribute"); + INamedTypeSymbol? compExaclyDependsOn = context.Compilation.Assembly.GetTypeByMetadataName("System.Runtime.CompilerServices.CompExactlyDependsOnAttribute"); - IntrinsicsAnalyzerOnLoadData onLoadData = new IntrinsicsAnalyzerOnLoadData(namedTypesToBeProtected, bypassReadyToRunAttribute, bypassReadyToRunForIntrinsicsHelperUse); + IntrinsicsAnalyzerOnLoadData onLoadData = new IntrinsicsAnalyzerOnLoadData(namedTypesToBeProtected, bypassReadyToRunAttribute, compExaclyDependsOn); // Find all types in the System.Runtime.Intrinsics namespace that have an IsSupported property that are NOT // directly in the System.Runtime.Intrinsics namespace @@ -374,12 +372,12 @@ private static INamedTypeSymbol[][] DecomposeConditionForIsSupportedGroups(Opera private static IEnumerable GetBypassForIntrinsicHelperUseList(ISymbol symbol, IntrinsicsAnalyzerOnLoadData onLoadData) { - var bypassReadyToRunForIntrinsicsHelperUse = onLoadData.BypassReadyToRunForIntrinsicsHelperUse; - if (bypassReadyToRunForIntrinsicsHelperUse != null) + var compExaclyDependsOn = onLoadData.CompExactlyDependsOn; + if (compExaclyDependsOn != null) { foreach (var attributeData in symbol.GetAttributes()) { - if (attributeData.AttributeClass.Equals(bypassReadyToRunForIntrinsicsHelperUse, SymbolEqualityComparer.Default)) + if (attributeData.AttributeClass.Equals(compExaclyDependsOn, SymbolEqualityComparer.Default)) { if (attributeData.ConstructorArguments[0].Value is INamedTypeSymbol attributeTypeSymbol) { @@ -488,7 +486,7 @@ private static void AnalyzeOperation(IOperation operation, IMethodSymbol methodS return; } - var bypassReadyToRunForIntrinsicsHelperUse = onLoadData.BypassReadyToRunForIntrinsicsHelperUse; + var compExaclyDependsOn = onLoadData.CompExactlyDependsOn; ISymbol? symbolThatMightHaveIntrinsicsHelperAttribute = methodSymbol; IOperation operationSearch = operation; @@ -516,11 +514,11 @@ private static void AnalyzeOperation(IOperation operation, IMethodSymbol methodS { ISymbol? attributeExplicitlyAllowsRelatedSymbol = null; ISymbol? attributeExplicitlyAllowsExactSymbol = null; - if ((bypassReadyToRunForIntrinsicsHelperUse != null) && symbolThatMightHaveIntrinsicsHelperAttribute != null) + if ((compExaclyDependsOn != null) && symbolThatMightHaveIntrinsicsHelperAttribute != null) { foreach (var attributeData in symbolThatMightHaveIntrinsicsHelperAttribute.GetAttributes()) { - if (attributeData.AttributeClass.Equals(bypassReadyToRunForIntrinsicsHelperUse, SymbolEqualityComparer.Default)) + if (attributeData.AttributeClass.Equals(compExaclyDependsOn, SymbolEqualityComparer.Default)) { if (attributeData.ConstructorArguments[0].Value is INamedTypeSymbol attributeTypeSymbol) { diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 4cc8abb81e3be0..8eb4af84efc9cd 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -749,7 +749,6 @@ - @@ -767,6 +766,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs index 09b0e1d40ec8bb..139407bd985668 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs @@ -352,7 +352,7 @@ public static unsafe OperationStatus DecodeFromUtf8InPlace(Span buffer, ou } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] + [CompExactlyDependsOn(typeof(Avx2))] private static unsafe void Avx2Decode(ref byte* srcBytes, ref byte* destBytes, byte* srcEnd, int sourceLength, int destLength, byte* srcStart, byte* destStart) { // If we have AVX2 support, pick off 32 bytes at a time for as long as we can, @@ -480,8 +480,8 @@ private static unsafe void Avx2Decode(ref byte* srcBytes, ref byte* destBytes, b } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] private static Vector128 SimdShuffle(Vector128 left, Vector128 right, Vector128 mask8F) { Debug.Assert((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian); @@ -495,8 +495,8 @@ private static Vector128 SimdShuffle(Vector128 left, Vector128 } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] + [CompExactlyDependsOn(typeof(Ssse3))] private static unsafe void Vector128Decode(ref byte* srcBytes, ref byte* destBytes, byte* srcEnd, int sourceLength, int destLength, byte* srcStart, byte* destStart) { Debug.Assert((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian); diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Encoder.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Encoder.cs index 1bb7e030964488..d1c8a0a1cc01e8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Encoder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Encoder.cs @@ -227,7 +227,7 @@ public static unsafe OperationStatus EncodeToUtf8InPlace(Span buffer, int } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] + [CompExactlyDependsOn(typeof(Avx2))] private static unsafe void Avx2Encode(ref byte* srcBytes, ref byte* destBytes, byte* srcEnd, int sourceLength, int destLength, byte* srcStart, byte* destStart) { // If we have AVX2 support, pick off 24 bytes at a time for as long as we can. @@ -398,8 +398,8 @@ private static unsafe void Avx2Encode(ref byte* srcBytes, ref byte* destBytes, b } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] private static unsafe void Vector128Encode(ref byte* srcBytes, ref byte* destBytes, byte* srcEnd, int sourceLength, int destLength, byte* srcStart, byte* destStart) { // If we have SSSE3 support, pick off 12 bytes at a time for as long as we can. diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.cs index dee2117bc146cd..8fd0da14bd6c62 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.cs @@ -1381,8 +1381,8 @@ private unsafe bool TryFormatX(Span destination, out int charsWrit } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] private static (Vector128, Vector128, Vector128) FormatGuidVector128Utf8(Guid value, bool useDashes) { Debug.Assert((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian); diff --git a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAnyAsciiSearcher.cs b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAnyAsciiSearcher.cs index 7375606b68594c..2eb493372066dc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAnyAsciiSearcher.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/IndexOfAnyAsciiSearcher.cs @@ -166,9 +166,9 @@ private static unsafe bool TryLastIndexOfAny(ref short searchSpace, in return false; } - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] internal static int IndexOfAnyVectorized(ref short searchSpace, int searchSpaceLength, Vector128 bitmap) where TNegator : struct, INegator where TOptimizations : struct, IOptimizations @@ -281,9 +281,9 @@ internal static int IndexOfAnyVectorized(ref short sea return -1; } - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] internal static int LastIndexOfAnyVectorized(ref short searchSpace, int searchSpaceLength, Vector128 bitmap) where TNegator : struct, INegator where TOptimizations : struct, IOptimizations @@ -396,9 +396,9 @@ internal static int LastIndexOfAnyVectorized(ref short return -1; } - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] internal static int IndexOfAnyVectorized(ref byte searchSpace, int searchSpaceLength, Vector128 bitmap) where TNegator : struct, INegator { @@ -506,9 +506,9 @@ internal static int IndexOfAnyVectorized(ref byte searchSpace, int sea return -1; } - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] internal static int LastIndexOfAnyVectorized(ref byte searchSpace, int searchSpaceLength, Vector128 bitmap) where TNegator : struct, INegator { @@ -616,9 +616,9 @@ internal static int LastIndexOfAnyVectorized(ref byte searchSpace, int return -1; } - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] internal static int IndexOfAnyVectorized(ref byte searchSpace, int searchSpaceLength, Vector128 bitmap0, Vector128 bitmap1) where TNegator : struct, INegator { @@ -727,9 +727,9 @@ internal static int IndexOfAnyVectorized(ref byte searchSpace, int sea return -1; } - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] internal static int LastIndexOfAnyVectorized(ref byte searchSpace, int searchSpaceLength, Vector128 bitmap0, Vector128 bitmap1) where TNegator : struct, INegator { @@ -839,9 +839,9 @@ internal static int LastIndexOfAnyVectorized(ref byte searchSpace, int } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] + [CompExactlyDependsOn(typeof(Sse2))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] private static Vector128 IndexOfAnyLookup(Vector128 source0, Vector128 source1, Vector128 bitmapLookup) where TNegator : struct, INegator where TOptimizations : struct, IOptimizations @@ -877,9 +877,9 @@ private static Vector128 IndexOfAnyLookup(Vector } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] private static Vector128 IndexOfAnyLookupCore(Vector128 source, Vector128 bitmapLookup) { // On X86, the Ssse3.Shuffle instruction will already perform an implicit 'AND 0xF' on the indices, so we can skip it. @@ -907,7 +907,7 @@ private static Vector128 IndexOfAnyLookupCore(Vector128 source, Vect } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] + [CompExactlyDependsOn(typeof(Avx2))] private static Vector256 IndexOfAnyLookup(Vector256 source0, Vector256 source1, Vector256 bitmapLookup) where TNegator : struct, INegator where TOptimizations : struct, IOptimizations @@ -928,7 +928,7 @@ private static Vector256 IndexOfAnyLookup(Vector } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] + [CompExactlyDependsOn(typeof(Avx2))] private static Vector256 IndexOfAnyLookupCore(Vector256 source, Vector256 bitmapLookup) { // See comments in IndexOfAnyLookupCore(Vector128) above for more details. @@ -940,9 +940,9 @@ private static Vector256 IndexOfAnyLookupCore(Vector256 source, Vect } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(PackedSimd))] private static Vector128 IndexOfAnyLookup(Vector128 source, Vector128 bitmapLookup0, Vector128 bitmapLookup1) where TNegator : struct, INegator { @@ -965,7 +965,7 @@ private static Vector128 IndexOfAnyLookup(Vector128 source } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] + [CompExactlyDependsOn(typeof(Avx2))] private static Vector256 IndexOfAnyLookup(Vector256 source, Vector256 bitmapLookup0, Vector256 bitmapLookup1) where TNegator : struct, INegator { @@ -1036,7 +1036,7 @@ private static unsafe int ComputeLastIndexOverlapped(ref T searchSp } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] + [CompExactlyDependsOn(typeof(Avx2))] private static unsafe int ComputeFirstIndex(ref T searchSpace, ref T current, Vector256 result) where TNegator : struct, INegator { @@ -1052,7 +1052,7 @@ private static unsafe int ComputeFirstIndex(ref T searchSpace, ref } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] + [CompExactlyDependsOn(typeof(Avx2))] private static unsafe int ComputeFirstIndexOverlapped(ref T searchSpace, ref T current0, ref T current1, Vector256 result) where TNegator : struct, INegator { @@ -1074,7 +1074,7 @@ private static unsafe int ComputeFirstIndexOverlapped(ref T searchS } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] + [CompExactlyDependsOn(typeof(Avx2))] private static unsafe int ComputeLastIndex(ref T searchSpace, ref T current, Vector256 result) where TNegator : struct, INegator { @@ -1090,7 +1090,7 @@ private static unsafe int ComputeLastIndex(ref T searchSpace, ref T } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] + [CompExactlyDependsOn(typeof(Avx2))] private static unsafe int ComputeLastIndexOverlapped(ref T searchSpace, ref T secondVector, Vector256 result) where TNegator : struct, INegator { @@ -1112,7 +1112,7 @@ private static unsafe int ComputeLastIndexOverlapped(ref T searchSp } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] + [CompExactlyDependsOn(typeof(Avx2))] private static Vector256 FixUpPackedVector256Result(Vector256 result) { Debug.Assert(Avx2.IsSupported); diff --git a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/ProbabilisticMap.cs b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/ProbabilisticMap.cs index 942c6c5d4aedbb..564e65f4d0dc11 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/ProbabilisticMap.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IndexOfAnyValues/ProbabilisticMap.cs @@ -107,7 +107,7 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(values)), values.Length); [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] + [CompExactlyDependsOn(typeof(Avx2))] private static Vector256 ContainsMask32CharsAvx2(Vector256 charMapLower, Vector256 charMapUpper, ref char searchSpace) { Vector256 source0 = Vector256.LoadUnsafe(ref searchSpace); @@ -128,7 +128,7 @@ private static Vector256 ContainsMask32CharsAvx2(Vector256 charMapLo } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] + [CompExactlyDependsOn(typeof(Avx2))] private static Vector256 IsCharBitSetAvx2(Vector256 charMapLower, Vector256 charMapUpper, Vector256 values) { // X86 doesn't have a logical right shift intrinsic for bytes: https://github.com/dotnet/runtime/issues/82564 @@ -146,8 +146,8 @@ private static Vector256 IsCharBitSetAvx2(Vector256 charMapLower, Ve } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] + [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] + [CompExactlyDependsOn(typeof(Sse2))] private static Vector128 ContainsMask16Chars(Vector128 charMapLower, Vector128 charMapUpper, ref char searchSpace) { Vector128 source0 = Vector128.LoadUnsafe(ref searchSpace); @@ -168,11 +168,11 @@ private static Vector128 ContainsMask16Chars(Vector128 charMapLower, } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] - [BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] - [BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] - [BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] - [BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] + [CompExactlyDependsOn(typeof(Sse2))] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] + [CompExactlyDependsOn(typeof(PackedSimd))] private static Vector128 IsCharBitSet(Vector128 charMapLower, Vector128 charMapUpper, Vector128 values) { // X86 doesn't have a logical right shift intrinsic for bytes: https://github.com/dotnet/runtime/issues/82564 @@ -362,8 +362,8 @@ internal static int LastIndexOfAny(ref uint charMap, ref char searchSp return -1; } - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse41))] + [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] + [CompExactlyDependsOn(typeof(Sse41))] private static int IndexOfAnyVectorized(ref uint charMap, ref char searchSpace, int searchSpaceLength, ReadOnlySpan values) { Debug.Assert(Sse41.IsSupported || AdvSimd.Arm64.IsSupported); diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix4x4.Impl.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix4x4.Impl.cs index 9d1244bef475f7..bf692d02f73d22 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix4x4.Impl.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix4x4.Impl.cs @@ -1055,7 +1055,7 @@ public static bool Invert(in Impl matrix, out Impl result) return SoftwareFallback(in matrix, out result); - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse))] + [CompExactlyDependsOn(typeof(Sse))] static bool SseImpl(in Impl matrix, out Impl result) { if (!Sse.IsSupported) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/BypassReadyToRunForIntrinsicsHelperUse.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CompExactlyDependsOn.cs similarity index 74% rename from src/libraries/System.Private.CoreLib/src/System/Runtime/BypassReadyToRunForIntrinsicsHelperUse.cs rename to src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CompExactlyDependsOn.cs index 20db6e12a809cf..3169bd22b42d18 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/BypassReadyToRunForIntrinsicsHelperUse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CompExactlyDependsOn.cs @@ -1,14 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace System.Runtime +namespace System.Runtime.CompilerServices { // Use this attribute to indicate that a function should only be compiled into a Ready2Run // binary if the associated type will always have a well defined value for its IsSupported property [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, AllowMultiple = true, Inherited = false)] - internal sealed class BypassReadyToRunForIntrinsicsHelperUseAttribute : Attribute + internal sealed class CompExactlyDependsOnAttribute : Attribute { - public BypassReadyToRunForIntrinsicsHelperUseAttribute(Type intrinsicsTypeUsedInHelperFunction) + public CompExactlyDependsOnAttribute(Type intrinsicsTypeUsedInHelperFunction) { IntrinsicsTypeUsedInHelperFunction = intrinsicsTypeUsedInHelperFunction; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CompExactlyDependsOnAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CompExactlyDependsOnAttribute.cs new file mode 100644 index 00000000000000..3169bd22b42d18 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CompExactlyDependsOnAttribute.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.CompilerServices +{ + // Use this attribute to indicate that a function should only be compiled into a Ready2Run + // binary if the associated type will always have a well defined value for its IsSupported property + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, AllowMultiple = true, Inherited = false)] + internal sealed class CompExactlyDependsOnAttribute : Attribute + { + public CompExactlyDependsOnAttribute(Type intrinsicsTypeUsedInHelperFunction) + { + IntrinsicsTypeUsedInHelperFunction = intrinsicsTypeUsedInHelperFunction; + } + + public Type IntrinsicsTypeUsedInHelperFunction { get; } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs index d6aa0266b9642e..a233eb2d51db84 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128.cs @@ -2444,10 +2444,10 @@ public static Vector128 Shuffle(Vector128 vector, Vector128 /// On hardware with support, indices are treated as modulo 16, and if the high bit is set, the result will be set to 0 for that element. /// On hardware with or support, this method behaves the same as Shuffle. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [BypassReadyToRunForIntrinsicsHelperUse(typeof(Ssse3))] - [BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd))] - [BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] - [BypassReadyToRunForIntrinsicsHelperUse(typeof(PackedSimd))] + [CompExactlyDependsOn(typeof(Ssse3))] + [CompExactlyDependsOn(typeof(AdvSimd))] + [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] + [CompExactlyDependsOn(typeof(PackedSimd))] internal static Vector128 ShuffleUnsafe(Vector128 vector, Vector128 indices) { if (Ssse3.IsSupported) @@ -3222,8 +3222,8 @@ internal static void SetUpperUnsafe(in this Vector128 vector, Vector64 } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] + [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] + [CompExactlyDependsOn(typeof(Sse2))] internal static Vector128 UnpackLow(Vector128 left, Vector128 right) { if (Sse2.IsSupported) @@ -3238,8 +3238,8 @@ internal static Vector128 UnpackLow(Vector128 left, Vector128 } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] + [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] + [CompExactlyDependsOn(typeof(Sse2))] internal static Vector128 UnpackHigh(Vector128 left, Vector128 right) { if (Sse2.IsSupported) @@ -3256,8 +3256,8 @@ internal static Vector128 UnpackHigh(Vector128 left, Vector128 // TODO: Make generic versions of these public, see https://github.com/dotnet/runtime/issues/82559 [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] + [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] + [CompExactlyDependsOn(typeof(Sse2))] internal static Vector128 AddSaturate(Vector128 left, Vector128 right) { if (Sse2.IsSupported) @@ -3272,8 +3272,8 @@ internal static Vector128 AddSaturate(Vector128 left, Vector128 SubtractSaturate(Vector128 left, Vector128 right) { if (Sse2.IsSupported) diff --git a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs index 0c2f1b8ca987ec..1851d1e26ffefa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.Packed.cs @@ -35,46 +35,46 @@ public static unsafe bool CanUsePackedIndexOf(T value) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] + [CompExactlyDependsOn(typeof(Sse2))] public static int IndexOf(ref char searchSpace, char value, int length) => IndexOf>(ref Unsafe.As(ref searchSpace), (short)value, length); [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] + [CompExactlyDependsOn(typeof(Sse2))] public static int IndexOfAnyExcept(ref char searchSpace, char value, int length) => IndexOf>(ref Unsafe.As(ref searchSpace), (short)value, length); [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] + [CompExactlyDependsOn(typeof(Sse2))] public static int IndexOfAny(ref char searchSpace, char value0, char value1, int length) => IndexOfAny>(ref Unsafe.As(ref searchSpace), (short)value0, (short)value1, length); [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] + [CompExactlyDependsOn(typeof(Sse2))] public static int IndexOfAnyExcept(ref char searchSpace, char value0, char value1, int length) => IndexOfAny>(ref Unsafe.As(ref searchSpace), (short)value0, (short)value1, length); [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] + [CompExactlyDependsOn(typeof(Sse2))] public static int IndexOfAny(ref char searchSpace, char value0, char value1, char value2, int length) => IndexOfAny>(ref Unsafe.As(ref searchSpace), (short)value0, (short)value1, (short)value2, length); [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] + [CompExactlyDependsOn(typeof(Sse2))] public static int IndexOfAnyExcept(ref char searchSpace, char value0, char value1, char value2, int length) => IndexOfAny>(ref Unsafe.As(ref searchSpace), (short)value0, (short)value1, (short)value2, length); [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] + [CompExactlyDependsOn(typeof(Sse2))] public static int IndexOfAnyInRange(ref char searchSpace, char lowInclusive, char rangeInclusive, int length) => IndexOfAnyInRange>(ref Unsafe.As(ref searchSpace), (short)lowInclusive, (short)rangeInclusive, length); [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] + [CompExactlyDependsOn(typeof(Sse2))] public static int IndexOfAnyExceptInRange(ref char searchSpace, char lowInclusive, char rangeInclusive, int length) => IndexOfAnyInRange>(ref Unsafe.As(ref searchSpace), (short)lowInclusive, (short)rangeInclusive, length); - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] + [CompExactlyDependsOn(typeof(Sse2))] public static bool Contains(ref short searchSpace, short value, int length) { Debug.Assert(CanUsePackedIndexOf(value)); @@ -228,7 +228,7 @@ public static bool Contains(ref short searchSpace, short value, int length) return false; } - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] + [CompExactlyDependsOn(typeof(Sse2))] private static int IndexOf(ref short searchSpace, short value, int length) where TNegator : struct, SpanHelpers.INegator { @@ -381,7 +381,7 @@ private static int IndexOf(ref short searchSpace, short value, int len return -1; } - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] + [CompExactlyDependsOn(typeof(Sse2))] private static int IndexOfAny(ref short searchSpace, short value0, short value1, int length) where TNegator : struct, SpanHelpers.INegator { @@ -543,7 +543,7 @@ private static int IndexOfAny(ref short searchSpace, short value0, sho return -1; } - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] + [CompExactlyDependsOn(typeof(Sse2))] private static int IndexOfAny(ref short searchSpace, short value0, short value1, short value2, int length) where TNegator : struct, SpanHelpers.INegator { @@ -708,7 +708,7 @@ private static int IndexOfAny(ref short searchSpace, short value0, sho return -1; } - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] + [CompExactlyDependsOn(typeof(Sse2))] private static int IndexOfAnyInRange(ref short searchSpace, short lowInclusive, short rangeInclusive, int length) where TNegator : struct, SpanHelpers.INegator { @@ -854,7 +854,7 @@ private static int IndexOfAnyInRange(ref short searchSpace, short lowI } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] + [CompExactlyDependsOn(typeof(Avx2))] private static Vector256 PackSources(Vector256 source0, Vector256 source1) { Debug.Assert(Avx2.IsSupported); @@ -866,7 +866,7 @@ private static Vector256 PackSources(Vector256 source0, Vector256 PackSources(Vector128 source0, Vector128 source1) { Debug.Assert(Sse2.IsSupported); @@ -896,7 +896,7 @@ private static int ComputeFirstIndex(ref short searchSpace, ref short current, V } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] + [CompExactlyDependsOn(typeof(Avx2))] private static int ComputeFirstIndex(ref short searchSpace, ref short current, Vector256 equals) { uint notEqualsElements = FixUpPackedVector256Result(equals).ExtractMostSignificantBits(); @@ -919,7 +919,7 @@ private static int ComputeFirstIndexOverlapped(ref short searchSpace, ref short } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] + [CompExactlyDependsOn(typeof(Avx2))] private static int ComputeFirstIndexOverlapped(ref short searchSpace, ref short current0, ref short current1, Vector256 equals) { uint notEqualsElements = FixUpPackedVector256Result(equals).ExtractMostSignificantBits(); @@ -934,7 +934,7 @@ private static int ComputeFirstIndexOverlapped(ref short searchSpace, ref short } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx2))] + [CompExactlyDependsOn(typeof(Avx2))] private static Vector256 FixUpPackedVector256Result(Vector256 result) { Debug.Assert(Avx2.IsSupported); diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Utility.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Utility.cs index 0965d9a90609a7..e835ddf0b5a2c6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Utility.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Utility.cs @@ -53,7 +53,7 @@ private static bool AllCharsInUInt64AreAscii(ulong value) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] + [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] private static int GetIndexOfFirstNonAsciiByteInLane_AdvSimd(Vector128 value, Vector128 bitmask) { if (!AdvSimd.Arm64.IsSupported || !BitConverter.IsLittleEndian) @@ -1479,7 +1479,7 @@ private static bool AllCharsInVectorAreAscii(Vector128 vector) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Avx))] + [CompExactlyDependsOn(typeof(Avx))] private static bool AllCharsInVectorAreAscii(Vector256 vector) where T : unmanaged { diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Latin1Utility.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Latin1Utility.cs index 18b81c8d4b2c8e..92f4c259c9f976 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Latin1Utility.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Latin1Utility.cs @@ -163,7 +163,7 @@ private static unsafe nuint GetIndexOfFirstNonLatin1Char_Default(char* pBuffer, goto Finish; } - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] + [CompExactlyDependsOn(typeof(Sse2))] private static unsafe nuint GetIndexOfFirstNonLatin1Char_Sse2(char* pBuffer, nuint bufferLength /* in chars */) { // This method contains logic optimized for both SSE2 and SSE41. Much of the logic in this method @@ -761,7 +761,7 @@ public static unsafe nuint NarrowUtf16ToLatin1(char* pUtf16Buffer, byte* pLatin1 goto Finish; } - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] + [CompExactlyDependsOn(typeof(Sse2))] private static unsafe nuint NarrowUtf16ToLatin1_Sse2(char* pUtf16Buffer, byte* pLatin1Buffer, nuint elementCount) { // This method contains logic optimized for both SSE2 and SSE41. Much of the logic in this method @@ -960,7 +960,7 @@ public static unsafe void WidenLatin1ToUtf16(byte* pLatin1Buffer, char* pUtf16Bu } } - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(Sse2))] + [CompExactlyDependsOn(typeof(Sse2))] private static unsafe void WidenLatin1ToUtf16_Sse2(byte* pLatin1Buffer, char* pUtf16Buffer, nuint elementCount) { // JIT turns the below into constants diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.Validation.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.Validation.cs index c24cdef18a8bb4..8818a96d16f107 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.Validation.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf16Utility.Validation.cs @@ -489,7 +489,7 @@ internal static unsafe partial class Utf16Utility } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] + [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] private static uint GetNonAsciiBytes(Vector128 value, Vector128 bitMask128) { Debug.Assert(AdvSimd.Arm64.IsSupported); diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.Validation.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.Validation.cs index 6cfd21a1e25a96..a542dad72b5c33 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.Validation.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Unicode/Utf8Utility.Validation.cs @@ -740,7 +740,7 @@ internal static unsafe partial class Utf8Utility } [MethodImpl(MethodImplOptions.AggressiveInlining)] - [System.Runtime.BypassReadyToRunForIntrinsicsHelperUse(typeof(AdvSimd.Arm64))] + [CompExactlyDependsOn(typeof(AdvSimd.Arm64))] private static ulong GetNonAsciiBytes(Vector128 value, Vector128 bitMask128) { if (!AdvSimd.Arm64.IsSupported || !BitConverter.IsLittleEndian) From 506f0533f8cf7eada37ede656f7d23f1d79a81ed Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 9 May 2023 15:14:47 -0700 Subject: [PATCH 10/19] Add analyzer tests and fixup issues from merging with main branch --- .../src/System/Buffers/Text/Base64Decoder.cs | 70 ++ .../SearchValues/IndexOfAnyAsciiSearcher.cs | 9 +- .../CSharpAnalyzerVerifier`1+Test.cs | 24 + .../CSharpAnalyzerVerifier`1.cs | 38 ++ ...rinsicsInSystemPrivateCoreLib.Tests.csproj | 24 + ...trinsicsInSystemPrivateCoreLibUnitTests.cs | 633 ++++++++++++++++++ 6 files changed, 796 insertions(+), 2 deletions(-) create mode 100644 src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/CSharpAnalyzerVerifier`1+Test.cs create mode 100644 src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/CSharpAnalyzerVerifier`1.cs create mode 100644 src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/IntrinsicsInSystemPrivateCoreLib.Tests.csproj create mode 100644 src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/IntrinsicsInSystemPrivateCoreLibUnitTests.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs index c63e948a13cfab..6a6c2f018d8c06 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs @@ -554,6 +554,76 @@ private static unsafe OperationStatus DecodeFromUtf8InPlaceCore(Span buffe } } + private static OperationStatus DecodeWithWhiteSpaceFromUtf8InPlace(Span utf8, ref int destIndex, int sourceIndex) + { + const int BlockSize = 4; + Span buffer = stackalloc byte[BlockSize]; + + OperationStatus status = OperationStatus.Done; + int localDestIndex = destIndex; + bool hasPaddingBeenProcessed = false; + int localBytesWritten = 0; + + while ((uint)sourceIndex < (uint)utf8.Length) + { + int bufferIdx = 0; + + while (bufferIdx < BlockSize) + { + if ((uint)sourceIndex >= (uint)utf8.Length) // TODO https://github.com/dotnet/runtime/issues/83349: move into the while condition once fixed + { + break; + } + + if (!IsWhiteSpace(utf8[sourceIndex])) + { + buffer[bufferIdx] = utf8[sourceIndex]; + bufferIdx++; + } + + sourceIndex++; + } + + if (bufferIdx == 0) + { + continue; + } + + if (bufferIdx != 4) + { + status = OperationStatus.InvalidData; + break; + } + + if (hasPaddingBeenProcessed) + { + // Padding has already been processed, a new valid block cannot be processed. + // Revert previous dest increment, since an invalid state followed. + localDestIndex -= localBytesWritten; + status = OperationStatus.InvalidData; + break; + } + + status = DecodeFromUtf8InPlaceCore(buffer, out localBytesWritten, out _); + localDestIndex += localBytesWritten; + hasPaddingBeenProcessed = localBytesWritten < 3; + + if (status != OperationStatus.Done) + { + break; + } + + // Write result to source span in place. + for (int i = 0; i < localBytesWritten; i++) + { + utf8[localDestIndex - localBytesWritten + i] = buffer[i]; + } + } + + destIndex = localDestIndex; + return status; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] [CompExactlyDependsOn(typeof(Avx2))] private static unsafe void Avx2Decode(ref byte* srcBytes, ref byte* destBytes, byte* srcEnd, int sourceLength, int destLength, byte* srcStart, byte* destStart) diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/IndexOfAnyAsciiSearcher.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/IndexOfAnyAsciiSearcher.cs index aae2b4e36e681a..e3da4bb1f4c996 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/IndexOfAnyAsciiSearcher.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/IndexOfAnyAsciiSearcher.cs @@ -1137,6 +1137,8 @@ internal interface IOptimizations { // Replace with Vector128.NarrowWithSaturation once https://github.com/dotnet/runtime/issues/75724 is implemented. [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CompExactlyDependsOn(typeof(Sse2))] + [CompExactlyDependsOn(typeof(PackedSimd))] public static Vector128 PackSources(Vector128 lower, Vector128 upper) { Vector128 lowerMin = Vector128.Min(lower, Vector128.Create((ushort)255)).AsInt16(); @@ -1148,7 +1150,7 @@ public static Vector128 PackSources(Vector128 lower, Vector128 PackSources(Vector256 lower, Vector256 upper) { @@ -1161,6 +1163,9 @@ public static Vector256 PackSources(Vector256 lower, Vector256 PackSources(Vector128 lower, Vector128 upper) { return @@ -1169,7 +1174,7 @@ public static Vector128 PackSources(Vector128 lower, Vector128 PackSources(Vector256 lower, Vector256 upper) { diff --git a/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/CSharpAnalyzerVerifier`1+Test.cs b/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/CSharpAnalyzerVerifier`1+Test.cs new file mode 100644 index 00000000000000..1372bc02caacde --- /dev/null +++ b/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/CSharpAnalyzerVerifier`1+Test.cs @@ -0,0 +1,24 @@ +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing.Verifiers; + +namespace IntrinsicsInSystemPrivateCoreLib.Test +{ + public static partial class CSharpAnalyzerVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + { + public class Test : CSharpAnalyzerTest + { + public Test() + { + SolutionTransforms.Add((solution, projectId) => + { + var compilationOptions = solution.GetProject(projectId).CompilationOptions; + solution = solution.WithProjectCompilationOptions(projectId, compilationOptions); + + return solution; + }); + } + } + } +} diff --git a/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/CSharpAnalyzerVerifier`1.cs b/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/CSharpAnalyzerVerifier`1.cs new file mode 100644 index 00000000000000..6677ef554399b9 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/CSharpAnalyzerVerifier`1.cs @@ -0,0 +1,38 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Testing.Verifiers; +using System.Threading; +using System.Threading.Tasks; + +namespace IntrinsicsInSystemPrivateCoreLib.Test +{ + public static partial class CSharpAnalyzerVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + { + /// + public static DiagnosticResult Diagnostic() + => CSharpAnalyzerVerifier.Diagnostic(); + + /// + public static DiagnosticResult Diagnostic(string diagnosticId) + => CSharpAnalyzerVerifier.Diagnostic(diagnosticId); + + /// + public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) + => CSharpAnalyzerVerifier.Diagnostic(descriptor); + + /// + public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected) + { + var test = new Test + { + TestCode = source, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(CancellationToken.None); + } + } +} diff --git a/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/IntrinsicsInSystemPrivateCoreLib.Tests.csproj b/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/IntrinsicsInSystemPrivateCoreLib.Tests.csproj new file mode 100644 index 00000000000000..4ba5d2a603c3ad --- /dev/null +++ b/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/IntrinsicsInSystemPrivateCoreLib.Tests.csproj @@ -0,0 +1,24 @@ + + + + $(NetCoreAppCurrent) + true + true + AnyCPU;x64 + + + + + + + + + + + + + + + + + diff --git a/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/IntrinsicsInSystemPrivateCoreLibUnitTests.cs b/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/IntrinsicsInSystemPrivateCoreLibUnitTests.cs new file mode 100644 index 00000000000000..f83ecfd3621ed3 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/IntrinsicsInSystemPrivateCoreLibUnitTests.cs @@ -0,0 +1,633 @@ +using Xunit; +using System.Threading.Tasks; +using VerifyCS = IntrinsicsInSystemPrivateCoreLib.Test.CSharpAnalyzerVerifier< + IntrinsicsInSystemPrivateCoreLib.IntrinsicsInSystemPrivateCoreLibAnalyzer>; + +namespace IntrinsicsInSystemPrivateCoreLib.Test +{ + public class IntrinsicsInSystemPrivateCoreLibUnitTest + { + string BoilerPlate = @" +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Diagnostics; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics.X86; +using System.Runtime.Intrinsics.Arm; +using System.Runtime.Intrinsics.Wasm; + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false, AllowMultiple = true)] + internal sealed class CompExactlyDependsOnAttribute : Attribute + { + public CompExactlyDependsOnAttribute(Type intrinsicsTypeUsedInHelperFunction) + { + } + } +} + +namespace System.Runtime.Intrinsics.X86 +{ + class Sse + { + public static bool IsSupported => true; + public static bool DoSomething() { return true; } + public class X64 + { + public static bool IsSupported => true; + public static bool DoSomethingX64() { return true; } + } + } + class Avx : Sse + { + public static bool IsSupported => true; + public static bool DoSomething() { return true; } + public class X64 + { + public static bool IsSupported => true; + public static bool DoSomethingX64() { return true; } + } + } + class Avx2 : Avx + { + public static bool IsSupported => true; + public static bool DoSomething() { return true; } + public class X64 + { + public static bool IsSupported => true; + public static bool DoSomethingX64() { return true; } + } + } +} +namespace System.Runtime.Intrinsics.Arm +{ + class ArmBase + { + public static bool IsSupported => true; + public static bool DoSomething() { return true; } + public class Arm64 + { + public static bool IsSupported => true; + public static bool DoSomethingArm64() { return true; } + } + } +} + +namespace System.Runtime.Intrinsics.Wasm +{ + class PackedSimd + { + public static bool IsSupported => true; + public static bool DoSomething() { return true; } + } +} + +"; + [Fact] + public async Task TestMethodUnprotectedUse() + { + var test = BoilerPlate + @" + namespace ConsoleApplication1 + { + class TypeName + { + static void FuncBad() + { + {|#0:Avx2.DoSomething()|}; + } + } + }"; + + var expected = VerifyCS.Diagnostic("IntrinsicsInSystemPrivateCoreLib").WithLocation(0).WithArguments("System.Runtime.Intrinsics.X86.Avx2"); + await VerifyCS.VerifyAnalyzerAsync(test, expected); + } + + [Fact] + public async Task TestMethodUnprotectedUseWithIntrinsicsHelperAttribute() + { + var test = BoilerPlate + @" + +namespace ConsoleApplication1 +{ + class TypeName + { + [CompExactlyDependsOn(typeof(Avx2))] + static void FuncGood() + { + Avx2.DoSomething(); + } + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(test); + } + + [Fact] + public async Task TestMethodUnprotectedUseWithIntrinsicsHelperAttributeComplex() + { + var test = BoilerPlate + @" + +namespace ConsoleApplication1 +{ + class TypeName + { + [CompExactlyDependsOn(typeof(Avx))] + [CompExactlyDependsOn(typeof(Avx2))] + static void FuncGood() + { + // This tests the behavior of a function which behaves differently when Avx2 is supported (Somehting like Vector128.ShuffleUnsafe) + if (Avx2.IsSupported) + Avx2.DoSomething(); + else + Avx.DoSomething(); + } + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(test); + } + + [Fact] + public async Task TestMethodUnprotectedUseInLocalFunctionWithIntrinsicsHelperAttributeNotOnLocalFunction() + { + var test = BoilerPlate + @" +namespace ConsoleApplication1 +{ + class TypeName + { + [CompExactlyDependsOn(typeof(Avx2))] + static void FuncBad() + { + LocalFunc(); + + static void LocalFunc() + { + {|#0:Avx2.DoSomething()|}; + } + } + } +}"; + + var expected = VerifyCS.Diagnostic("IntrinsicsInSystemPrivateCoreLib").WithLocation(0).WithArguments("System.Runtime.Intrinsics.X86.Avx2"); + await VerifyCS.VerifyAnalyzerAsync(test, expected); + } + + [Fact] + public async Task TestMethodUnprotectedUseInLambdaWithIntrinsicsHelperAttributeOnOuterFunction() + { + var test = BoilerPlate + @" + +namespace ConsoleApplication1 +{ + class TypeName + { + [CompExactlyDependsOn(typeof(Avx2))] + static void FuncBad() + { + Action act = () => + { + {|#0:Avx2.DoSomething()|}; + }; + act(); + } + } +}"; + + var expected = VerifyCS.Diagnostic("IntrinsicsInSystemPrivateCoreLib").WithLocation(0).WithArguments("System.Runtime.Intrinsics.X86.Avx2"); + await VerifyCS.VerifyAnalyzerAsync(test, expected); + } + + [Fact] + public async Task TestMethodUnprotectedUseInLocalFunctionWithIntrinsicsHelperAttributeOnLocalFunction() + { + var test = BoilerPlate + @" +namespace ConsoleApplication1 +{ + class TypeName + { + static void FuncBad() + { + [CompExactlyDependsOn(typeof(Avx2))] + static void LocalFunc() + { + Avx2.DoSomething(); + } + + if (Avx2.IsSupported) + LocalFunc(); + } + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(test); + } + + [Fact] + public async Task TestMethodUnprotectedNestedTypeUse() + { + var test = BoilerPlate + @" + namespace ConsoleApplication1 + { + class TypeName + { + static void FuncBad() + { + {|#0:Avx2.X64.DoSomethingX64()|}; + } + } + }"; + + var expected = VerifyCS.Diagnostic("IntrinsicsInSystemPrivateCoreLib").WithLocation(0).WithArguments("System.Runtime.Intrinsics.X86.Avx2.X64"); + await VerifyCS.VerifyAnalyzerAsync(test, expected); + } + + [Fact] + public async Task TestMethodWithIfStatement() + { + var test = BoilerPlate + @" + namespace ConsoleApplication1 + { + class TypeName + { + static void FuncGood() + { + if (Avx2.IsSupported) + Avx2.DoSomething(); + } + } + }"; + + await VerifyCS.VerifyAnalyzerAsync(test); + } + + + [Fact] + public async Task TestMethodWithIfStatementButWithInadequateHelperMethodAttribute() + { + var test = BoilerPlate + @" + namespace ConsoleApplication1 + { + class TypeName + { + [CompExactlyDependsOn(typeof(Avx))] + static void FuncBad() + { + if ({|#0:Avx2.IsSupported|}) + Avx2.DoSomething(); + } + } + }"; + + var expected = VerifyCS.Diagnostic("IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough").WithLocation(0).WithArguments("System.Runtime.Intrinsics.X86.Avx"); + await VerifyCS.VerifyAnalyzerAsync(test, expected); + } + + [Fact] + public async Task TestMethodWithIfStatementButWithAdequateHelperMethodAttribute() + { + var test = BoilerPlate + @" + namespace ConsoleApplication1 + { + class TypeName + { + [CompExactlyDependsOn(typeof(Avx2))] + static void FuncBad() + { + if (Avx2.IsSupported) + Avx2.DoSomething(); + } + } + }"; + + await VerifyCS.VerifyAnalyzerAsync(test); + } + + [Fact] + public async Task TestMethodWithIfStatementWithNestedAndBaseTypeLookupRequired() + { + var test = BoilerPlate + @" + namespace ConsoleApplication1 + { + class TypeName + { + static void FuncGood() + { + if (Avx2.X64.IsSupported) + Sse.DoSomething(); + } + } + }"; + + await VerifyCS.VerifyAnalyzerAsync(test); + } + + [Fact] + public async Task TestMethodWithTernaryOperator() + { + var test = BoilerPlate + @" + namespace ConsoleApplication1 + { + class TypeName + { + static bool FuncGood() + { + return Avx2.IsSupported ? Avx2.DoSomething() : false; + } + } + }"; + + await VerifyCS.VerifyAnalyzerAsync(test); + } + + [Fact] + public async Task TestMethodWithIfStatementWithOrOperationCase() + { + var test = BoilerPlate + @" + namespace ConsoleApplication1 + { + class TypeName + { + static void FuncGood() + { + if (ArmBase.IsSupported || (Avx2.IsSupported && BitConverter.IsLittleEndian)) + { + if (ArmBase.IsSupported) + ArmBase.DoSomething(); + else + Avx2.DoSomething(); + + if (Avx2.IsSupported) + Avx2.DoSomething(); + else + ArmBase.DoSomething(); + } + } + } + }"; + + await VerifyCS.VerifyAnalyzerAsync(test); + } + + [Fact] + public async Task TestMethodWithIfStatementWithOrOperationCaseWithImplicationProcessingRequired() + { + var test = BoilerPlate + @" + namespace ConsoleApplication1 + { + class TypeName + { + static void FuncGood() + { + if (ArmBase.Arm64.IsSupported || (Avx2.IsSupported && BitConverter.IsLittleEndian)) + { + if (ArmBase.IsSupported) + ArmBase.DoSomething(); + else + Avx2.DoSomething(); + + if (Avx2.IsSupported) + Avx2.DoSomething(); + else + ArmBase.DoSomething(); + } + } + } + }"; + + await VerifyCS.VerifyAnalyzerAsync(test); + } + + [Fact] + public async Task TestMethodWithIfStatementAroundLocalFunctionDefinition() + { + var test = BoilerPlate + @" + + namespace ConsoleApplication1 + { + class TypeName + { + static void FuncGood() + { + if (Avx2.IsSupported) + { + LocalFunction(); + + // Local functions should cause an error to be reported, as they are NOT the same function from a runtime point of view + void LocalFunction() + { + {|#0:Avx2.DoSomething()|}; + } + } + } + } + }"; + + var expected = VerifyCS.Diagnostic("IntrinsicsInSystemPrivateCoreLib").WithLocation(0).WithArguments("System.Runtime.Intrinsics.X86.Avx2"); + await VerifyCS.VerifyAnalyzerAsync(test, expected); + } + + [Fact] + public async Task TestMethodWithIfStatementAroundLambdaFunctionDefinition() + { + var test = BoilerPlate + @" + namespace ConsoleApplication1 + { + class TypeName + { + static void FuncGood() + { + if (Avx2.IsSupported) + { + // Lambda functions should cause an error to be reported, as they are NOT the same function from a runtime point of view + Action a = () => + { + {|#0:Avx2.DoSomething()|}; + }; + } + } + } + }"; + + var expected = VerifyCS.Diagnostic("IntrinsicsInSystemPrivateCoreLib").WithLocation(0).WithArguments("System.Runtime.Intrinsics.X86.Avx2"); + await VerifyCS.VerifyAnalyzerAsync(test, expected); + } + + [Fact] + public async Task TestHelperMethodsCanOnlyBeCalledWithAppropriateIsSupportedChecksError() + { + var test = BoilerPlate + @" + namespace ConsoleApplication1 + { + class TypeName + { + [CompExactlyDependsOn(typeof(Avx))] + static void FuncHelper() + { + } + + [CompExactlyDependsOn(typeof(Avx))] + [CompExactlyDependsOn(typeof(ArmBase))] + static void FuncHelper2() + { + } + + static bool SomeIrrelevantProperty => true; + + static void FuncBad() + { + {|#0:FuncHelper()|}; + if (Avx2.IsSupported || ArmBase.IsSupported) + { + {|#1:FuncHelper()|}; + } + + if ({|#3:(Avx.IsSupported || ArmBase.IsSupported) && PackedSimd.IsSupported|}) + { + {|#2:FuncHelper2()|}; + } + + + if (Avx.IsSupported || (SomeIrrelevantProperty && ArmBase.IsSupported)) + { + {|#4:FuncHelper()|}; + } + } + } + }"; + + var expected = VerifyCS.Diagnostic("IntrinsicsInSystemPrivateCoreLibHelper").WithLocation(0).WithArguments("ConsoleApplication1.TypeName.FuncHelper()"); + var expected2 = VerifyCS.Diagnostic("IntrinsicsInSystemPrivateCoreLibHelper").WithLocation(1).WithArguments("ConsoleApplication1.TypeName.FuncHelper()"); + var expected3 = VerifyCS.Diagnostic("IntrinsicsInSystemPrivateCoreLibHelper").WithLocation(2).WithArguments("ConsoleApplication1.TypeName.FuncHelper2()"); + var expected4 = VerifyCS.Diagnostic("IntrinsicsInSystemPrivateCoreLibConditionParsing").WithLocation(3); + var expected5 = VerifyCS.Diagnostic("IntrinsicsInSystemPrivateCoreLibHelper").WithLocation(4).WithArguments("ConsoleApplication1.TypeName.FuncHelper()"); + await VerifyCS.VerifyAnalyzerAsync(test, expected, expected2, expected3, expected4, expected5); + } + [Fact] + public async Task TestHelperMethodsCanOnlyBeCalledWithAppropriateIsSupportedChecksSuccess() + { + var test = BoilerPlate + @" + namespace ConsoleApplication1 + { + class TypeName + { + [CompExactlyDependsOn(typeof(Avx))] + [CompExactlyDependsOn(typeof(ArmBase))] + static void FuncHelper() + { + } + + static bool SomeIrrelevantProperty => true; + static void FuncGood() + { + if (Avx2.IsSupported) + { + FuncHelper(); + } + if (ArmBase.IsSupported) + { + FuncHelper(); + } + if (Avx2.IsSupported || ArmBase.IsSupported) + { + FuncHelper(); + } + if ((Avx2.IsSupported || ArmBase.IsSupported) && SomeIrrelevantProperty) + { + FuncHelper(); + } + } + } + }"; + + await VerifyCS.VerifyAnalyzerAsync(test); + } + + [Fact] + public async Task TestHelperMethodsUnrelatedPropertyDoesntHelp() + { + var test = BoilerPlate + @" + namespace ConsoleApplication1 + { + class TypeName + { + [CompExactlyDependsOn(typeof(Avx))] + [CompExactlyDependsOn(typeof(ArmBase))] + static void FuncHelper() + { + } + + static bool HelperIsSupported => true; + + static void FuncBad() + { + if (HelperIsSupported) + { + {|#0:FuncHelper()|}; + } + } + } + }"; + + var expected = VerifyCS.Diagnostic("IntrinsicsInSystemPrivateCoreLibHelper").WithLocation(0).WithArguments("ConsoleApplication1.TypeName.FuncHelper()"); + await VerifyCS.VerifyAnalyzerAsync(test, expected); + } + + [Fact] + public async Task TestHelperMethodsWithHelperProperty() + { + var test = BoilerPlate + @" + namespace ConsoleApplication1 + { + class TypeName + { + [CompExactlyDependsOn(typeof(Avx))] + [CompExactlyDependsOn(typeof(ArmBase))] + static void FuncHelper() + { + } + + static bool HelperIsSupported => Avx.IsSupported || ArmBase.IsSupported; + + static void FuncGood() + { + if (HelperIsSupported) + { + FuncHelper(); + } + } + } + }"; + + await VerifyCS.VerifyAnalyzerAsync(test); + } + + + [Fact] + public async Task TestMethodUseOfIntrinsicsFromWithinOtherMethodOnIntrinsicType() + { + var test = @" +namespace System.Runtime.Intrinsics.X86 +{ + class Sse + { + public static bool IsSupported => true; + public static bool DoSomething() { return true; } + public static bool DoSomethingElse() { return !Sse.DoSomething(); } + public class X64 + { + public static bool IsSupported => true; + public static bool DoSomethingX64() { return !Sse.DoSomething(); } + } + } +} +"; + + await VerifyCS.VerifyAnalyzerAsync(test); + } + } +} From 2e33ae4cb8ecc622eea5f54c0c9b28b2e72e89a5 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 9 May 2023 16:44:08 -0700 Subject: [PATCH 11/19] Disable tests on Mono due to Mono runtime bug --- .../IntrinsicsInSystemPrivateCoreLibUnitTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/IntrinsicsInSystemPrivateCoreLibUnitTests.cs b/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/IntrinsicsInSystemPrivateCoreLibUnitTests.cs index f83ecfd3621ed3..6c783c7ae3aeb4 100644 --- a/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/IntrinsicsInSystemPrivateCoreLibUnitTests.cs +++ b/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/IntrinsicsInSystemPrivateCoreLibUnitTests.cs @@ -5,6 +5,7 @@ namespace IntrinsicsInSystemPrivateCoreLib.Test { + [ActiveIssue("https://github.com/dotnet/runtime/issues/60650", TestRuntimes.Mono)] public class IntrinsicsInSystemPrivateCoreLibUnitTest { string BoilerPlate = @" From 8a69798301a507b827c6dca1c80c167e4deb5381 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 10 May 2023 12:42:01 -0700 Subject: [PATCH 12/19] Handle incorrect namespace for the CompExactlyDependsOnAttribute and don't pass a null InstructionSetSupport into the ShouldSkipCompilation logic, just feed it around everywhere. --- .../Compiler/ReadyToRunCompilerContext.cs | 6 +++++- .../Compiler/ReadyToRunLibraryRootProvider.cs | 4 +++- .../Compiler/ReadyToRunProfilingRootProvider.cs | 4 +++- .../Compiler/ReadyToRunVisibilityRootProvider.cs | 4 +++- .../Compiler/ReadyToRunXmlRootProvider.cs | 4 +++- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 15 +++++++++------ src/coreclr/tools/aot/crossgen2/Program.cs | 4 ++-- 7 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs index 88a74a9ebc2f00..50abf1ec58747c 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs @@ -37,10 +37,12 @@ public partial class ReadyToRunCompilerContext : CompilerTypeSystemContext private VectorOfTFieldLayoutAlgorithm _vectorOfTFieldLayoutAlgorithm; private VectorFieldLayoutAlgorithm _vectorFieldLayoutAlgorithm; private Int128FieldLayoutAlgorithm _int128FieldLayoutAlgorithm; + private readonly InstructionSetSupport _instructionSetSupport; - public ReadyToRunCompilerContext(TargetDetails details, SharedGenericsMode genericsMode, bool bubbleIncludesCorelib, CompilerTypeSystemContext oldTypeSystemContext = null) + public ReadyToRunCompilerContext(TargetDetails details, SharedGenericsMode genericsMode, bool bubbleIncludesCorelib, InstructionSetSupport instructionSetSupport, CompilerTypeSystemContext oldTypeSystemContext = null) : base(details, genericsMode) { + _instructionSetSupport = instructionSetSupport; _r2rFieldLayoutAlgorithm = new ReadyToRunMetadataFieldLayoutAlgorithm(); _systemObjectFieldLayoutAlgorithm = new SystemObjectFieldLayoutAlgorithm(_r2rFieldLayoutAlgorithm); @@ -67,6 +69,8 @@ public ReadyToRunCompilerContext(TargetDetails details, SharedGenericsMode gener } } + public InstructionSetSupport InstructionSetSupport { get { return _instructionSetSupport; } } + public override FieldLayoutAlgorithm GetLayoutAlgorithmForType(DefType type) { if (type.IsObject) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunLibraryRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunLibraryRootProvider.cs index ff62a907bde2cb..dd977c2e437bd7 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunLibraryRootProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunLibraryRootProvider.cs @@ -16,10 +16,12 @@ namespace ILCompiler public class ReadyToRunLibraryRootProvider : ICompilationRootProvider { private EcmaModule _module; + private InstructionSetSupport _instructionSetSupport; public ReadyToRunLibraryRootProvider(EcmaModule module) { _module = module; + _instructionSetSupport = ((ReadyToRunCompilerContext)module.Context).InstructionSetSupport; } public void AddCompilationRoots(IRootingServiceProvider rootProvider) @@ -60,7 +62,7 @@ private void RootMethods(MetadataType type, string reason, IRootingServiceProvid try { - if (!CorInfoImpl.ShouldSkipCompilation(null, method)) + if (!CorInfoImpl.ShouldSkipCompilation(_instructionSetSupport, method)) { CheckCanGenerateMethod(methodToRoot); rootProvider.AddCompilationRoot(methodToRoot, rootMinimalDependencies: false, reason: reason); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProfilingRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProfilingRootProvider.cs index 1dc1dce5a1b010..f586d8ab7e3f43 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProfilingRootProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunProfilingRootProvider.cs @@ -19,11 +19,13 @@ public class ReadyToRunProfilingRootProvider : ICompilationRootProvider { private EcmaModule _module; private IEnumerable _profileData; + private InstructionSetSupport _instructionSetSupport; public ReadyToRunProfilingRootProvider(EcmaModule module, ProfileDataManager profileDataManager) { _module = module; _profileData = profileDataManager.GetInputProfileDataMethodsForModule(module); + _instructionSetSupport = ((ReadyToRunCompilerContext)module.Context).InstructionSetSupport; } public void AddCompilationRoots(IRootingServiceProvider rootProvider) @@ -61,7 +63,7 @@ public void AddCompilationRoots(IRootingServiceProvider rootProvider) if (containsSignatureVariables) continue; - if (!CorInfoImpl.ShouldSkipCompilation(null, method)) + if (!CorInfoImpl.ShouldSkipCompilation(_instructionSetSupport, method)) { ReadyToRunLibraryRootProvider.CheckCanGenerateMethod(method); rootProvider.AddCompilationRoot(method, rootMinimalDependencies: true, reason: "Profile triggered method"); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunVisibilityRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunVisibilityRootProvider.cs index 31ab3dcb2c1245..6772f366e22fbc 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunVisibilityRootProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunVisibilityRootProvider.cs @@ -16,10 +16,12 @@ namespace ILCompiler public class ReadyToRunVisibilityRootProvider : ICompilationRootProvider { private EcmaModule _module; + private InstructionSetSupport _instructionSetSupport; public ReadyToRunVisibilityRootProvider(EcmaModule module) { _module = module; + _instructionSetSupport = ((ReadyToRunCompilerContext)module.Context).InstructionSetSupport; } public void AddCompilationRoots(IRootingServiceProvider rootProvider) @@ -102,7 +104,7 @@ private void RootMethods(MetadataType type, string reason, IRootingServiceProvid try { - if (!CorInfoImpl.ShouldSkipCompilation(null, method)) + if (!CorInfoImpl.ShouldSkipCompilation(_instructionSetSupport, method)) { ReadyToRunLibraryRootProvider.CheckCanGenerateMethod(methodToRoot); rootProvider.AddCompilationRoot(methodToRoot, rootMinimalDependencies: false, reason: reason); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunXmlRootProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunXmlRootProvider.cs index f952060d9500a5..4ea3ab2b3b5ca6 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunXmlRootProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunXmlRootProvider.cs @@ -80,11 +80,13 @@ private class CompilationRootProvider : ProcessLinkerXmlBase private const string NamespaceElementName = "namespace"; private const string _preserve = "preserve"; private readonly IRootingServiceProvider _rootingServiceProvider; + private InstructionSetSupport _instructionSetSupport; public CompilationRootProvider(IRootingServiceProvider provider, TypeSystemContext context, Stream documentStream, ManifestResource resource, ModuleDesc owningModule, string xmlDocumentLocation) : base(null , context, documentStream, resource, owningModule, xmlDocumentLocation, ImmutableDictionary.Empty) { _rootingServiceProvider = provider; + _instructionSetSupport = ((ReadyToRunCompilerContext)owningModule.Context).InstructionSetSupport; } public void ProcessXml() => ProcessXml(false); @@ -141,7 +143,7 @@ private void RootMethod(MethodDesc method) try { - if (!CorInfoImpl.ShouldSkipCompilation(null, method)) + if (!CorInfoImpl.ShouldSkipCompilation(_instructionSetSupport, method)) { ReadyToRunLibraryRootProvider.CheckCanGenerateMethod(methodToRoot); _rootingServiceProvider.AddCompilationRoot(methodToRoot, rootMinimalDependencies: false, reason: "Linker XML descriptor"); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 96a84511f69a63..73c5d253a38e74 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -537,7 +537,7 @@ public static bool ShouldCodeNotBeCompiledIntoFinalImage(InstructionSetSupport i var handle = ecmaMethod.Handle; - List bypassForHelperIntrinsicsList = null; + List compExactlyDependsOnList = null; foreach (var attributeHandle in metadataReader.GetMethodDefinition(handle).GetCustomAttributes()) { @@ -551,6 +551,9 @@ public static bool ShouldCodeNotBeCompiledIntoFinalImage(InstructionSetSupport i { return true; } + } + else if (metadataReader.StringComparer.Equals(namespaceHandle, "System.Runtime.CompilerServices")) + { if (metadataReader.StringComparer.Equals(nameHandle, "CompExactlyDependsOnAttribute")) { var customAttribute = metadataReader.GetCustomAttribute(attributeHandle); @@ -562,22 +565,22 @@ public static bool ShouldCodeNotBeCompiledIntoFinalImage(InstructionSetSupport i TypeDesc typeForBypass = fixedArguments[0].Value as TypeDesc; if (typeForBypass != null) { - if (bypassForHelperIntrinsicsList == null) - bypassForHelperIntrinsicsList = new List(); + if (compExactlyDependsOnList == null) + compExactlyDependsOnList = new List(); - bypassForHelperIntrinsicsList.Add(typeForBypass); + compExactlyDependsOnList.Add(typeForBypass); } } } } - if (instructionSetSupport != null && bypassForHelperIntrinsicsList != null && bypassForHelperIntrinsicsList.Count > 0) + if (compExactlyDependsOnList != null && compExactlyDependsOnList.Count > 0) { // Default to true, and set to false if at least of the types is actually supported in the current environment, and none of the // intrinisc types are in an opportunistic state. bool doBypass = true; - foreach (var intrinsicType in bypassForHelperIntrinsicsList) + foreach (var intrinsicType in compExactlyDependsOnList) { InstructionSet instructionSet = InstructionSetParser.LookupPlatformIntrinsicInstructionSet(intrinsicType.Context.Target.Architecture, intrinsicType); if (instructionSet == InstructionSet.ILLEGAL) diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index 2eb91173d9f04f..639e5551e96940 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -120,7 +120,7 @@ public int Run() // // Initialize type system context // - _typeSystemContext = new ReadyToRunCompilerContext(targetDetails, genericsMode, versionBubbleIncludesCoreLib); + _typeSystemContext = new ReadyToRunCompilerContext(targetDetails, genericsMode, versionBubbleIncludesCoreLib, instructionSetSupport); string compositeRootPath = Get(_command.CompositeRootPath); @@ -262,7 +262,7 @@ public int Run() { bool singleCompilationVersionBubbleIncludesCoreLib = versionBubbleIncludesCoreLib || (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0); - typeSystemContext = new ReadyToRunCompilerContext(targetDetails, genericsMode, singleCompilationVersionBubbleIncludesCoreLib, _typeSystemContext); + typeSystemContext = new ReadyToRunCompilerContext(targetDetails, genericsMode, singleCompilationVersionBubbleIncludesCoreLib, _typeSystemContext.InstructionSetSupport, _typeSystemContext); typeSystemContext.InputFilePaths = singleCompilationInputFilePaths; typeSystemContext.ReferenceFilePaths = referenceFilePaths; typeSystemContext.SetSystemModule((EcmaModule)typeSystemContext.GetModuleForSimpleName(systemModuleName)); From 4e413bd7f6b3a0a74ec1a31c5f2332ca31f1f427 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 15 May 2023 09:40:48 -0700 Subject: [PATCH 13/19] With the analyzer, negative checks are problematic, so re-order some of them. Also we needed some new pragma warning disables to make the new logic work --- .../SearchValues/IndexOfAnyAsciiSearcher.cs | 16 ++- .../src/System/Text/Ascii.Equality.cs | 132 +++++++++--------- 2 files changed, 77 insertions(+), 71 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/IndexOfAnyAsciiSearcher.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/IndexOfAnyAsciiSearcher.cs index ddc9ceaef3aaba..87de3a404b84e7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/IndexOfAnyAsciiSearcher.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/IndexOfAnyAsciiSearcher.cs @@ -179,7 +179,7 @@ internal static int IndexOfAnyVectorized(ref short sea { ref short currentSearchSpace = ref searchSpace; -#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else clause is semantically equivalent +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The behavior of the rest of the function remains the same if Avx2.IsSupported is false if (Avx2.IsSupported && searchSpaceLength > 2 * Vector128.Count) #pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { @@ -236,7 +236,9 @@ internal static int IndexOfAnyVectorized(ref short sea Vector128 bitmap = bitmapRef._lower; +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The behavior of the rest of the function remains the same if Avx2.IsSupported is false if (!Avx2.IsSupported && searchSpaceLength > 2 * Vector128.Count) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { // Process the input in chunks of 16 characters (2 * Vector128). // We're mainly interested in a single byte of each character, and the core lookup operates on a Vector128. @@ -408,7 +410,7 @@ internal static int IndexOfAnyVectorized(ref byte searchSpace, int sea { ref byte currentSearchSpace = ref searchSpace; -#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else clause is semantically equivalent +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The behavior of the rest of the function remains the same if Avx2.IsSupported is false if (Avx2.IsSupported && searchSpaceLength > Vector128.Count) #pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { @@ -518,7 +520,7 @@ internal static int LastIndexOfAnyVectorized(ref byte searchSpace, int { ref byte currentSearchSpace = ref Unsafe.Add(ref searchSpace, searchSpaceLength); -#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else clause is semantically equivalent +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The behavior of the rest of the function remains the same if Avx2.IsSupported is false if (Avx2.IsSupported && searchSpaceLength > Vector128.Count) #pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { @@ -628,7 +630,7 @@ internal static int IndexOfAnyVectorizedAnyByte(ref byte searchSpace, { ref byte currentSearchSpace = ref searchSpace; -#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else clause is semantically equivalent +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The behavior of the rest of the function remains the same if Avx2.IsSupported is false if (Avx2.IsSupported && searchSpaceLength > Vector128.Count) #pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { @@ -685,7 +687,9 @@ internal static int IndexOfAnyVectorizedAnyByte(ref byte searchSpace, Vector128 bitmap0 = bitmapsRef._lower._lower; Vector128 bitmap1 = bitmapsRef._upper._lower; +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The behavior of the rest of the function remains the same if Avx2.IsSupported is false if (!Avx2.IsSupported && searchSpaceLength > Vector128.Count) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { // Process the input in chunks of 16 bytes. // If the input length is a multiple of 16, don't consume the last 16 characters in this loop. @@ -740,7 +744,7 @@ internal static int LastIndexOfAnyVectorizedAnyByte(ref byte searchSpa { ref byte currentSearchSpace = ref Unsafe.Add(ref searchSpace, searchSpaceLength); -#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The else clause is semantically equivalent +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The behavior of the rest of the function remains the same if Avx2.IsSupported is false if (Avx2.IsSupported && searchSpaceLength > Vector128.Count) { #pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough @@ -797,7 +801,9 @@ internal static int LastIndexOfAnyVectorizedAnyByte(ref byte searchSpa Vector128 bitmap0 = bitmapsRef._lower._lower; Vector128 bitmap1 = bitmapsRef._upper._lower; +#pragma warning disable IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough // The behavior of the rest of the function remains the same if Avx2.IsSupported is false if (!Avx2.IsSupported && searchSpaceLength > Vector128.Count) +#pragma warning restore IntrinsicsInSystemPrivateCoreLibAttributeNotSpecificEnough { // Process the input in chunks of 16 bytes. // If the input length is a multiple of 16, don't consume the last 16 characters in this loop. diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Equality.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Equality.cs index d857b1236dd0e4..5a9e9ef09cfb14 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Equality.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Ascii.Equality.cs @@ -61,38 +61,37 @@ private static bool Equals(ref TLeft left, ref TRight ri } } } - else if (!Vector256.IsHardwareAccelerated || length < (uint)Vector256.Count) + else if (Avx.IsSupported && length >= (uint)Vector256.Count) { ref TLeft currentLeftSearchSpace = ref left; - ref TLeft oneVectorAwayFromLeftEnd = ref Unsafe.Add(ref currentLeftSearchSpace, length - TLoader.Count128); + ref TLeft oneVectorAwayFromLeftEnd = ref Unsafe.Add(ref currentLeftSearchSpace, length - TLoader.Count256); ref TRight currentRightSearchSpace = ref right; - ref TRight oneVectorAwayFromRightEnd = ref Unsafe.Add(ref currentRightSearchSpace, length - (uint)Vector128.Count); + ref TRight oneVectorAwayFromRightEnd = ref Unsafe.Add(ref currentRightSearchSpace, length - (uint)Vector256.Count); - Vector128 leftValues; - Vector128 rightValues; + Vector256 leftValues; + Vector256 rightValues; // Loop until either we've finished all elements or there's less than a vector's-worth remaining. do { - // it's OK to widen the bytes, it's NOT OK to narrow the chars (we could loose some information) - leftValues = TLoader.Load128(ref currentLeftSearchSpace); - rightValues = Vector128.LoadUnsafe(ref currentRightSearchSpace); + leftValues = TLoader.Load256(ref currentLeftSearchSpace); + rightValues = Vector256.LoadUnsafe(ref currentRightSearchSpace); if (leftValues != rightValues || !AllCharsInVectorAreAscii(leftValues | rightValues)) { return false; } - currentRightSearchSpace = ref Unsafe.Add(ref currentRightSearchSpace, (uint)Vector128.Count); - currentLeftSearchSpace = ref Unsafe.Add(ref currentLeftSearchSpace, TLoader.Count128); + currentRightSearchSpace = ref Unsafe.Add(ref currentRightSearchSpace, Vector256.Count); + currentLeftSearchSpace = ref Unsafe.Add(ref currentLeftSearchSpace, TLoader.Count256); } while (!Unsafe.IsAddressGreaterThan(ref currentRightSearchSpace, ref oneVectorAwayFromRightEnd)); // If any elements remain, process the last vector in the search space. - if (length % (uint)Vector128.Count != 0) + if (length % (uint)Vector256.Count != 0) { - leftValues = TLoader.Load128(ref oneVectorAwayFromLeftEnd); - rightValues = Vector128.LoadUnsafe(ref oneVectorAwayFromRightEnd); + leftValues = TLoader.Load256(ref oneVectorAwayFromLeftEnd); + rightValues = Vector256.LoadUnsafe(ref oneVectorAwayFromRightEnd); if (leftValues != rightValues || !AllCharsInVectorAreAscii(leftValues | rightValues)) { @@ -103,34 +102,35 @@ private static bool Equals(ref TLeft left, ref TRight ri else { ref TLeft currentLeftSearchSpace = ref left; - ref TLeft oneVectorAwayFromLeftEnd = ref Unsafe.Add(ref currentLeftSearchSpace, length - TLoader.Count256); + ref TLeft oneVectorAwayFromLeftEnd = ref Unsafe.Add(ref currentLeftSearchSpace, length - TLoader.Count128); ref TRight currentRightSearchSpace = ref right; - ref TRight oneVectorAwayFromRightEnd = ref Unsafe.Add(ref currentRightSearchSpace, length - (uint)Vector256.Count); + ref TRight oneVectorAwayFromRightEnd = ref Unsafe.Add(ref currentRightSearchSpace, length - (uint)Vector128.Count); - Vector256 leftValues; - Vector256 rightValues; + Vector128 leftValues; + Vector128 rightValues; // Loop until either we've finished all elements or there's less than a vector's-worth remaining. do { - leftValues = TLoader.Load256(ref currentLeftSearchSpace); - rightValues = Vector256.LoadUnsafe(ref currentRightSearchSpace); + // it's OK to widen the bytes, it's NOT OK to narrow the chars (we could loose some information) + leftValues = TLoader.Load128(ref currentLeftSearchSpace); + rightValues = Vector128.LoadUnsafe(ref currentRightSearchSpace); if (leftValues != rightValues || !AllCharsInVectorAreAscii(leftValues | rightValues)) { return false; } - currentRightSearchSpace = ref Unsafe.Add(ref currentRightSearchSpace, Vector256.Count); - currentLeftSearchSpace = ref Unsafe.Add(ref currentLeftSearchSpace, TLoader.Count256); + currentRightSearchSpace = ref Unsafe.Add(ref currentRightSearchSpace, (uint)Vector128.Count); + currentLeftSearchSpace = ref Unsafe.Add(ref currentLeftSearchSpace, TLoader.Count128); } while (!Unsafe.IsAddressGreaterThan(ref currentRightSearchSpace, ref oneVectorAwayFromRightEnd)); // If any elements remain, process the last vector in the search space. - if (length % (uint)Vector256.Count != 0) + if (length % (uint)Vector128.Count != 0) { - leftValues = TLoader.Load256(ref oneVectorAwayFromLeftEnd); - rightValues = Vector256.LoadUnsafe(ref oneVectorAwayFromRightEnd); + leftValues = TLoader.Load128(ref oneVectorAwayFromLeftEnd); + rightValues = Vector128.LoadUnsafe(ref oneVectorAwayFromRightEnd); if (leftValues != rightValues || !AllCharsInVectorAreAscii(leftValues | rightValues)) { @@ -206,73 +206,72 @@ private static bool EqualsIgnoreCase(ref TLeft left, ref } } } - else if (!Vector256.IsHardwareAccelerated || length < (uint)Vector256.Count) + else if (Avx.IsSupported && length >= (uint)Vector256.Count) { ref TLeft currentLeftSearchSpace = ref left; - ref TLeft oneVectorAwayFromLeftEnd = ref Unsafe.Add(ref currentLeftSearchSpace, length - TLoader.Count128); + ref TLeft oneVectorAwayFromLeftEnd = ref Unsafe.Add(ref currentLeftSearchSpace, length - TLoader.Count256); ref TRight currentRightSearchSpace = ref right; - ref TRight oneVectorAwayFromRightEnd = ref Unsafe.Add(ref currentRightSearchSpace, length - (uint)Vector128.Count); + ref TRight oneVectorAwayFromRightEnd = ref Unsafe.Add(ref currentRightSearchSpace, length - (uint)Vector256.Count); - Vector128 leftValues; - Vector128 rightValues; + Vector256 leftValues; + Vector256 rightValues; - Vector128 loweringMask = Vector128.Create(TRight.CreateTruncating(0x20)); - Vector128 vecA = Vector128.Create(TRight.CreateTruncating('a')); - Vector128 vecZMinusA = Vector128.Create(TRight.CreateTruncating(('z' - 'a'))); + Vector256 loweringMask = Vector256.Create(TRight.CreateTruncating(0x20)); + Vector256 vecA = Vector256.Create(TRight.CreateTruncating('a')); + Vector256 vecZMinusA = Vector256.Create(TRight.CreateTruncating(('z' - 'a'))); // Loop until either we've finished all elements or there's less than a vector's-worth remaining. do { - // it's OK to widen the bytes, it's NOT OK to narrow the chars (we could loose some information) - leftValues = TLoader.Load128(ref currentLeftSearchSpace); - rightValues = Vector128.LoadUnsafe(ref currentRightSearchSpace); + leftValues = TLoader.Load256(ref currentLeftSearchSpace); + rightValues = Vector256.LoadUnsafe(ref currentRightSearchSpace); if (!AllCharsInVectorAreAscii(leftValues | rightValues)) { return false; } - Vector128 notEquals = ~Vector128.Equals(leftValues, rightValues); + Vector256 notEquals = ~Vector256.Equals(leftValues, rightValues); - if (notEquals != Vector128.Zero) + if (notEquals != Vector256.Zero) { // not exact match leftValues |= loweringMask; rightValues |= loweringMask; - if (Vector128.GreaterThanAny((leftValues - vecA) & notEquals, vecZMinusA) || leftValues != rightValues) + if (Vector256.GreaterThanAny((leftValues - vecA) & notEquals, vecZMinusA) || leftValues != rightValues) { return false; // first input isn't in [A-Za-z], and not exact match of lowered } } - currentRightSearchSpace = ref Unsafe.Add(ref currentRightSearchSpace, (uint)Vector128.Count); - currentLeftSearchSpace = ref Unsafe.Add(ref currentLeftSearchSpace, TLoader.Count128); + currentRightSearchSpace = ref Unsafe.Add(ref currentRightSearchSpace, (uint)Vector256.Count); + currentLeftSearchSpace = ref Unsafe.Add(ref currentLeftSearchSpace, TLoader.Count256); } while (!Unsafe.IsAddressGreaterThan(ref currentRightSearchSpace, ref oneVectorAwayFromRightEnd)); // If any elements remain, process the last vector in the search space. - if (length % (uint)Vector128.Count != 0) + if (length % (uint)Vector256.Count != 0) { - leftValues = TLoader.Load128(ref oneVectorAwayFromLeftEnd); - rightValues = Vector128.LoadUnsafe(ref oneVectorAwayFromRightEnd); + leftValues = TLoader.Load256(ref oneVectorAwayFromLeftEnd); + rightValues = Vector256.LoadUnsafe(ref oneVectorAwayFromRightEnd); if (!AllCharsInVectorAreAscii(leftValues | rightValues)) { return false; } - Vector128 notEquals = ~Vector128.Equals(leftValues, rightValues); + Vector256 notEquals = ~Vector256.Equals(leftValues, rightValues); - if (notEquals != Vector128.Zero) + if (notEquals != Vector256.Zero) { // not exact match leftValues |= loweringMask; rightValues |= loweringMask; - if (Vector128.GreaterThanAny((leftValues - vecA) & notEquals, vecZMinusA) || leftValues != rightValues) + if (Vector256.GreaterThanAny((leftValues - vecA) & notEquals, vecZMinusA) || leftValues != rightValues) { return false; // first input isn't in [A-Za-z], and not exact match of lowered } @@ -282,69 +281,70 @@ private static bool EqualsIgnoreCase(ref TLeft left, ref else { ref TLeft currentLeftSearchSpace = ref left; - ref TLeft oneVectorAwayFromLeftEnd = ref Unsafe.Add(ref currentLeftSearchSpace, length - TLoader.Count256); + ref TLeft oneVectorAwayFromLeftEnd = ref Unsafe.Add(ref currentLeftSearchSpace, length - TLoader.Count128); ref TRight currentRightSearchSpace = ref right; - ref TRight oneVectorAwayFromRightEnd = ref Unsafe.Add(ref currentRightSearchSpace, length - (uint)Vector256.Count); + ref TRight oneVectorAwayFromRightEnd = ref Unsafe.Add(ref currentRightSearchSpace, length - (uint)Vector128.Count); - Vector256 leftValues; - Vector256 rightValues; + Vector128 leftValues; + Vector128 rightValues; - Vector256 loweringMask = Vector256.Create(TRight.CreateTruncating(0x20)); - Vector256 vecA = Vector256.Create(TRight.CreateTruncating('a')); - Vector256 vecZMinusA = Vector256.Create(TRight.CreateTruncating(('z' - 'a'))); + Vector128 loweringMask = Vector128.Create(TRight.CreateTruncating(0x20)); + Vector128 vecA = Vector128.Create(TRight.CreateTruncating('a')); + Vector128 vecZMinusA = Vector128.Create(TRight.CreateTruncating(('z' - 'a'))); // Loop until either we've finished all elements or there's less than a vector's-worth remaining. do { - leftValues = TLoader.Load256(ref currentLeftSearchSpace); - rightValues = Vector256.LoadUnsafe(ref currentRightSearchSpace); + // it's OK to widen the bytes, it's NOT OK to narrow the chars (we could loose some information) + leftValues = TLoader.Load128(ref currentLeftSearchSpace); + rightValues = Vector128.LoadUnsafe(ref currentRightSearchSpace); if (!AllCharsInVectorAreAscii(leftValues | rightValues)) { return false; } - Vector256 notEquals = ~Vector256.Equals(leftValues, rightValues); + Vector128 notEquals = ~Vector128.Equals(leftValues, rightValues); - if (notEquals != Vector256.Zero) + if (notEquals != Vector128.Zero) { // not exact match leftValues |= loweringMask; rightValues |= loweringMask; - if (Vector256.GreaterThanAny((leftValues - vecA) & notEquals, vecZMinusA) || leftValues != rightValues) + if (Vector128.GreaterThanAny((leftValues - vecA) & notEquals, vecZMinusA) || leftValues != rightValues) { return false; // first input isn't in [A-Za-z], and not exact match of lowered } } - currentRightSearchSpace = ref Unsafe.Add(ref currentRightSearchSpace, (uint)Vector256.Count); - currentLeftSearchSpace = ref Unsafe.Add(ref currentLeftSearchSpace, TLoader.Count256); + currentRightSearchSpace = ref Unsafe.Add(ref currentRightSearchSpace, (uint)Vector128.Count); + currentLeftSearchSpace = ref Unsafe.Add(ref currentLeftSearchSpace, TLoader.Count128); } while (!Unsafe.IsAddressGreaterThan(ref currentRightSearchSpace, ref oneVectorAwayFromRightEnd)); // If any elements remain, process the last vector in the search space. - if (length % (uint)Vector256.Count != 0) + if (length % (uint)Vector128.Count != 0) { - leftValues = TLoader.Load256(ref oneVectorAwayFromLeftEnd); - rightValues = Vector256.LoadUnsafe(ref oneVectorAwayFromRightEnd); + leftValues = TLoader.Load128(ref oneVectorAwayFromLeftEnd); + rightValues = Vector128.LoadUnsafe(ref oneVectorAwayFromRightEnd); if (!AllCharsInVectorAreAscii(leftValues | rightValues)) { return false; } - Vector256 notEquals = ~Vector256.Equals(leftValues, rightValues); + Vector128 notEquals = ~Vector128.Equals(leftValues, rightValues); - if (notEquals != Vector256.Zero) + if (notEquals != Vector128.Zero) { // not exact match leftValues |= loweringMask; rightValues |= loweringMask; - if (Vector256.GreaterThanAny((leftValues - vecA) & notEquals, vecZMinusA) || leftValues != rightValues) + if (Vector128.GreaterThanAny((leftValues - vecA) & notEquals, vecZMinusA) || leftValues != rightValues) { return false; // first input isn't in [A-Za-z], and not exact match of lowered } From 994ff3e36d428c2e9f00d369182a91249d710a25 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 15 May 2023 09:40:57 -0700 Subject: [PATCH 14/19] Code review feedback --- ...ntrinsicsInSystemPrivateCoreLibAnalyzer.cs | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs b/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs index dfb1bf599b4722..dda902f9c7ed72 100644 --- a/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs +++ b/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs @@ -119,11 +119,11 @@ private sealed class IntrinsicsAnalyzerOnLoadData { public IntrinsicsAnalyzerOnLoadData(HashSet namedTypesToBeProtected, INamedTypeSymbol? bypassReadyToRunAttribute, - INamedTypeSymbol? compExaclyDependsOn) + INamedTypeSymbol? compExactlyDependsOn) { NamedTypesToBeProtected = namedTypesToBeProtected; BypassReadyToRunAttribute = bypassReadyToRunAttribute; - CompExactlyDependsOn = compExaclyDependsOn; + CompExactlyDependsOn = compExactlyDependsOn; } public readonly HashSet NamedTypesToBeProtected; public readonly INamedTypeSymbol? BypassReadyToRunAttribute; @@ -139,9 +139,9 @@ public override void Initialize(AnalysisContext context) HashSet namedTypesToBeProtected = new HashSet(SymbolEqualityComparer.Default); INamespaceSymbol systemRuntimeIntrinsicsNamespace = GetNamespace(context.Compilation.Assembly, "System", "Runtime", "Intrinsics"); INamedTypeSymbol? bypassReadyToRunAttribute = context.Compilation.Assembly.GetTypeByMetadataName("System.Runtime.BypassReadyToRunAttribute"); - INamedTypeSymbol? compExaclyDependsOn = context.Compilation.Assembly.GetTypeByMetadataName("System.Runtime.CompilerServices.CompExactlyDependsOnAttribute"); + INamedTypeSymbol? compExactlyDependsOn = context.Compilation.Assembly.GetTypeByMetadataName("System.Runtime.CompilerServices.CompExactlyDependsOnAttribute"); - IntrinsicsAnalyzerOnLoadData onLoadData = new IntrinsicsAnalyzerOnLoadData(namedTypesToBeProtected, bypassReadyToRunAttribute, compExaclyDependsOn); + IntrinsicsAnalyzerOnLoadData onLoadData = new IntrinsicsAnalyzerOnLoadData(namedTypesToBeProtected, bypassReadyToRunAttribute, compExactlyDependsOn); // Find all types in the System.Runtime.Intrinsics namespace that have an IsSupported property that are NOT // directly in the System.Runtime.Intrinsics namespace @@ -370,14 +370,14 @@ private static INamedTypeSymbol[][] DecomposeConditionForIsSupportedGroups(Opera return Array.Empty(); } - private static IEnumerable GetBypassForIntrinsicHelperUseList(ISymbol symbol, IntrinsicsAnalyzerOnLoadData onLoadData) + private static IEnumerable GetCompExactlyDependsOnUseList(ISymbol symbol, IntrinsicsAnalyzerOnLoadData onLoadData) { - var compExaclyDependsOn = onLoadData.CompExactlyDependsOn; - if (compExaclyDependsOn != null) + var compExactlyDependsOn = onLoadData.CompExactlyDependsOn; + if (compExactlyDependsOn != null) { foreach (var attributeData in symbol.GetAttributes()) { - if (attributeData.AttributeClass.Equals(compExaclyDependsOn, SymbolEqualityComparer.Default)) + if (attributeData.AttributeClass.Equals(compExactlyDependsOn, SymbolEqualityComparer.Default)) { if (attributeData.ConstructorArguments[0].Value is INamedTypeSymbol attributeTypeSymbol) { @@ -400,7 +400,7 @@ private static bool ConditionAllowsSymbol(ISymbol symbolOfInvokeTarget, INamedTy if (symbolOfInvokeTarget.ContainingSymbol.Equals(symbol, SymbolEqualityComparer.Default)) return true; - foreach (var helperForType in GetBypassForIntrinsicHelperUseList(symbolOfInvokeTarget, onLoadData)) + foreach (var helperForType in GetCompExactlyDependsOnUseList(symbolOfInvokeTarget, onLoadData)) { if (helperForType.Equals(symbol, SymbolEqualityComparer.Default)) return true; @@ -477,7 +477,7 @@ private static void AnalyzeOperation(IOperation operation, IMethodSymbol methodS if (!methodNeedsProtectionWithIsSupported) { - if (GetBypassForIntrinsicHelperUseList(symbol, onLoadData).Any()) + if (GetCompExactlyDependsOnUseList(symbol, onLoadData).Any()) methodNeedsProtectionWithIsSupported = true; } @@ -486,22 +486,22 @@ private static void AnalyzeOperation(IOperation operation, IMethodSymbol methodS return; } - var compExaclyDependsOn = onLoadData.CompExactlyDependsOn; + var compExactlyDependsOn = onLoadData.CompExactlyDependsOn; - ISymbol? symbolThatMightHaveIntrinsicsHelperAttribute = methodSymbol; + ISymbol? symbolThatMightHaveCompExactlyDependsOnAttribute = methodSymbol; IOperation operationSearch = operation; while (operationSearch != null) { if (operationSearch.Kind == OperationKind.AnonymousFunction) { - symbolThatMightHaveIntrinsicsHelperAttribute = null; + symbolThatMightHaveCompExactlyDependsOnAttribute = null; break; } if (operationSearch.Kind == OperationKind.LocalFunction) { - // Assign symbolThatMightHaveIntrinsicsHelperAttribute to the symbol for the LocalFunction + // Assign symbolThatMightHaveCompExactlyDependsOnAttribute to the symbol for the LocalFunction ILocalFunctionOperation localFunctionOperation = (ILocalFunctionOperation)operationSearch; - symbolThatMightHaveIntrinsicsHelperAttribute = localFunctionOperation.Symbol; + symbolThatMightHaveCompExactlyDependsOnAttribute = localFunctionOperation.Symbol; break; } @@ -514,11 +514,11 @@ private static void AnalyzeOperation(IOperation operation, IMethodSymbol methodS { ISymbol? attributeExplicitlyAllowsRelatedSymbol = null; ISymbol? attributeExplicitlyAllowsExactSymbol = null; - if ((compExaclyDependsOn != null) && symbolThatMightHaveIntrinsicsHelperAttribute != null) + if ((compExactlyDependsOn != null) && symbolThatMightHaveCompExactlyDependsOnAttribute != null) { - foreach (var attributeData in symbolThatMightHaveIntrinsicsHelperAttribute.GetAttributes()) + foreach (var attributeData in symbolThatMightHaveCompExactlyDependsOnAttribute.GetAttributes()) { - if (attributeData.AttributeClass.Equals(compExaclyDependsOn, SymbolEqualityComparer.Default)) + if (attributeData.AttributeClass.Equals(compExactlyDependsOn, SymbolEqualityComparer.Default)) { if (attributeData.ConstructorArguments[0].Value is INamedTypeSymbol attributeTypeSymbol) { @@ -552,9 +552,9 @@ private static void AnalyzeOperation(IOperation operation, IMethodSymbol methodS } } - if (symbolThatMightHaveIntrinsicsHelperAttribute != null) + if (symbolThatMightHaveCompExactlyDependsOnAttribute != null) { - foreach (var attributeTypeSymbol in GetBypassForIntrinsicHelperUseList(symbolThatMightHaveIntrinsicsHelperAttribute, onLoadData)) + foreach (var attributeTypeSymbol in GetCompExactlyDependsOnUseList(symbolThatMightHaveCompExactlyDependsOnAttribute, onLoadData)) { if (ConditionAllowsSymbol(symbol, attributeTypeSymbol, onLoadData)) { From 8ed51a169e880ce2341cca963952a4b280bba1aa Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 15 May 2023 11:52:50 -0700 Subject: [PATCH 15/19] Update src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs Co-authored-by: Jeremy Koritzinsky --- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 73c5d253a38e74..bb23738a4dc621 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -576,8 +576,8 @@ public static bool ShouldCodeNotBeCompiledIntoFinalImage(InstructionSetSupport i if (compExactlyDependsOnList != null && compExactlyDependsOnList.Count > 0) { - // Default to true, and set to false if at least of the types is actually supported in the current environment, and none of the - // intrinisc types are in an opportunistic state. + // Default to true, and set to false if at least one of the types is actually supported in the current environment, and none of the + // intrinsic types are in an opportunistic state. bool doBypass = true; foreach (var intrinsicType in compExactlyDependsOnList) From 9442ebee2f63c020b99b404e58cd289dc8a2de4f Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 15 May 2023 11:53:01 -0700 Subject: [PATCH 16/19] Update src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/IntrinsicsInSystemPrivateCoreLib.Tests.csproj Co-authored-by: Jeremy Koritzinsky --- .../IntrinsicsInSystemPrivateCoreLib.Tests.csproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/IntrinsicsInSystemPrivateCoreLib.Tests.csproj b/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/IntrinsicsInSystemPrivateCoreLib.Tests.csproj index 4ba5d2a603c3ad..790a0c81a36356 100644 --- a/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/IntrinsicsInSystemPrivateCoreLib.Tests.csproj +++ b/src/libraries/System.Private.CoreLib/tests/IntrinsicsInSystemPrivatecoreLibAnalyzer.Tests/IntrinsicsInSystemPrivateCoreLib.Tests.csproj @@ -2,9 +2,6 @@ $(NetCoreAppCurrent) - true - true - AnyCPU;x64 From fba92ebfb1ac6c1c56a8e1ffcd0db8ecf3f2eb3a Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 15 May 2023 11:53:09 -0700 Subject: [PATCH 17/19] Update src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs Co-authored-by: Jeremy Koritzinsky --- .../gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs b/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs index dda902f9c7ed72..7735788e71642a 100644 --- a/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs +++ b/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs @@ -440,15 +440,7 @@ private static bool TypeSymbolAllowsTypeSymbol(INamedTypeSymbol namedTypeToCheck private static INamespaceSymbol? SymbolToNamespaceSymbol(ISymbol symbol) { - if (symbol.ContainingType != null) - return SymbolToNamespaceSymbol(symbol.ContainingType); - - if (symbol.ContainingNamespace != null) - { - return symbol.ContainingNamespace; - } - - return null; + return symbol.ContainingNamespace; } private static void AnalyzeOperation(IOperation operation, IMethodSymbol methodSymbol, OperationAnalysisContext context, IntrinsicsAnalyzerOnLoadData onLoadData) { From fcc38ca7ddcbb6d56766ddfa13c31fde53399764 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 15 May 2023 11:54:45 -0700 Subject: [PATCH 18/19] Update IntrinsicsInSystemPrivateCoreLibAnalyzer.cs --- .../gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs b/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs index 7735788e71642a..75f702eaca8edf 100644 --- a/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs +++ b/src/libraries/System.Private.CoreLib/gen/IntrinsicsInSystemPrivateCoreLibAnalyzer.cs @@ -452,7 +452,7 @@ private static void AnalyzeOperation(IOperation operation, IMethodSymbol methodS } bool methodNeedsProtectionWithIsSupported = false; -#pragma warning disable RS1024 +#pragma warning disable RS1024 // The hashset is constructed with the correct comparer if (onLoadData.NamedTypesToBeProtected.Contains(symbol.ContainingSymbol)) { methodNeedsProtectionWithIsSupported = true; From 62c5e58c14fdad9adb4bee3d3a39e24757b230d7 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 15 May 2023 15:43:56 -0700 Subject: [PATCH 19/19] Use auto-implemented get-only property --- .../Compiler/ReadyToRunCompilerContext.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs index 50abf1ec58747c..6eed36223b0992 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs @@ -37,12 +37,11 @@ public partial class ReadyToRunCompilerContext : CompilerTypeSystemContext private VectorOfTFieldLayoutAlgorithm _vectorOfTFieldLayoutAlgorithm; private VectorFieldLayoutAlgorithm _vectorFieldLayoutAlgorithm; private Int128FieldLayoutAlgorithm _int128FieldLayoutAlgorithm; - private readonly InstructionSetSupport _instructionSetSupport; public ReadyToRunCompilerContext(TargetDetails details, SharedGenericsMode genericsMode, bool bubbleIncludesCorelib, InstructionSetSupport instructionSetSupport, CompilerTypeSystemContext oldTypeSystemContext = null) : base(details, genericsMode) { - _instructionSetSupport = instructionSetSupport; + InstructionSetSupport = instructionSetSupport; _r2rFieldLayoutAlgorithm = new ReadyToRunMetadataFieldLayoutAlgorithm(); _systemObjectFieldLayoutAlgorithm = new SystemObjectFieldLayoutAlgorithm(_r2rFieldLayoutAlgorithm); @@ -69,7 +68,7 @@ public ReadyToRunCompilerContext(TargetDetails details, SharedGenericsMode gener } } - public InstructionSetSupport InstructionSetSupport { get { return _instructionSetSupport; } } + public InstructionSetSupport InstructionSetSupport { get; } public override FieldLayoutAlgorithm GetLayoutAlgorithmForType(DefType type) {