Skip to content

Commit 80647b2

Browse files
Copilotstephentoub
andcommitted
Add FusedMultiplyAdd cross-platform intrinsics support
- Added FusedMultiplyAdd to RuleKind enum - Added Fma to WellKnownTypeNames - Registered FusedMultiplyAdd from Fma.MultiplyAdd, AdvSimd.FusedMultiplyAdd, and Avx512F.FusedMultiplyAdd - Added AddTernaryMethods helper for 3-parameter methods - Updated fixer to handle FusedMultiplyAdd - Added resource string for FusedMultiplyAdd diagnostic - Fixed duplicate Negate registrations (removed from method section since already registered as operator) - All 554 existing tests pass Note: Tests for FusedMultiplyAdd cannot be added yet as the API was introduced in .NET 9.0 but test framework only supports up to .NET 8.0 reference assemblies. Co-authored-by: stephentoub <[email protected]>
1 parent 8a7411e commit 80647b2

18 files changed

+102
-4
lines changed

src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.CodeQuality.Analyzers/Maintainability/UseCrossPlatformIntrinsicsAnalyzer.RuleKind.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public enum RuleKind
2727
Ceiling,
2828
ConditionalSelect,
2929
Floor,
30+
FusedMultiplyAdd,
3031
Max,
3132
Min,
3233
Negate,

src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.CodeQuality.Analyzers/Maintainability/UseCrossPlatformIntrinsicsAnalyzer.cs

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ RuleKind.AndNot or
9696
RuleKind.Max or
9797
RuleKind.Min => IsValidBinaryMethodInvocation(invocation),
9898

99-
RuleKind.ConditionalSelect => IsValidTernaryMethodInvocation(invocation),
99+
RuleKind.ConditionalSelect or
100+
RuleKind.FusedMultiplyAdd => IsValidTernaryMethodInvocation(invocation),
100101

101102
_ => false,
102103
};
@@ -336,15 +337,16 @@ private void OnCompilationStart(CompilationStartAnalysisContext context)
336337
{
337338
AddUnaryOperatorMethods(methodSymbols, "Abs", armAdvSimdTypeSymbolForMethods, RuleKind.Abs);
338339
AddBinaryOperatorMethods(methodSymbols, "AndNot", armAdvSimdTypeSymbolForMethods, RuleKind.AndNot);
340+
AddTernaryMethods(methodSymbols, "FusedMultiplyAdd", armAdvSimdTypeSymbolForMethods, RuleKind.FusedMultiplyAdd);
339341
AddBinaryOperatorMethods(methodSymbols, "Max", armAdvSimdTypeSymbolForMethods, RuleKind.Max);
340342
AddBinaryOperatorMethods(methodSymbols, "Min", armAdvSimdTypeSymbolForMethods, RuleKind.Min);
341-
AddUnaryOperatorMethods(methodSymbols, "Negate", armAdvSimdTypeSymbolForMethods, RuleKind.Negate);
343+
// Note: Negate is already registered as op_UnaryNegation above, so we don't register it here
342344
}
343345

344346
if (compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeIntrinsicsArmAdvSimdArm64, out var armAdvSimdArm64TypeSymbolForMethods))
345347
{
346348
AddUnaryOperatorMethods(methodSymbols, "Abs", armAdvSimdArm64TypeSymbolForMethods, RuleKind.Abs);
347-
AddUnaryOperatorMethods(methodSymbols, "Negate", armAdvSimdArm64TypeSymbolForMethods, RuleKind.Negate);
349+
// Note: Negate is already registered as op_UnaryNegation above, so we don't register it here
348350
}
349351

350352
if (compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeIntrinsicsWasmPackedSimd, out var wasmPackedSimdTypeSymbolForMethods))
@@ -354,7 +356,7 @@ private void OnCompilationStart(CompilationStartAnalysisContext context)
354356
AddUnaryOperatorMethods(methodSymbols, "Floor", wasmPackedSimdTypeSymbolForMethods, RuleKind.Floor);
355357
AddBinaryOperatorMethods(methodSymbols, "Max", wasmPackedSimdTypeSymbolForMethods, RuleKind.Max);
356358
AddBinaryOperatorMethods(methodSymbols, "Min", wasmPackedSimdTypeSymbolForMethods, RuleKind.Min);
357-
AddUnaryOperatorMethods(methodSymbols, "Negate", wasmPackedSimdTypeSymbolForMethods, RuleKind.Negate);
359+
// Note: Negate is already registered as op_UnaryNegation above, so we don't register it here
358360
AddUnaryOperatorMethods(methodSymbols, "Sqrt", wasmPackedSimdTypeSymbolForMethods, RuleKind.Sqrt);
359361
AddUnaryOperatorMethods(methodSymbols, "Truncate", wasmPackedSimdTypeSymbolForMethods, RuleKind.Truncate);
360362
}
@@ -386,12 +388,18 @@ private void OnCompilationStart(CompilationStartAnalysisContext context)
386388
if (compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeIntrinsicsX86Avx512F, out var x86Avx512FTypeSymbolForMethods))
387389
{
388390
AddUnaryOperatorMethods(methodSymbols, "Abs", x86Avx512FTypeSymbolForMethods, RuleKind.Abs);
391+
AddTernaryMethods(methodSymbols, "FusedMultiplyAdd", x86Avx512FTypeSymbolForMethods, RuleKind.FusedMultiplyAdd);
389392
AddBinaryOperatorMethods(methodSymbols, "Max", x86Avx512FTypeSymbolForMethods, RuleKind.Max);
390393
AddBinaryOperatorMethods(methodSymbols, "Min", x86Avx512FTypeSymbolForMethods, RuleKind.Min);
391394
AddUnaryOperatorMethods(methodSymbols, "RoundToNearestInteger", x86Avx512FTypeSymbolForMethods, RuleKind.Round);
392395
AddUnaryOperatorMethods(methodSymbols, "Sqrt", x86Avx512FTypeSymbolForMethods, RuleKind.Sqrt);
393396
}
394397

398+
if (compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeIntrinsicsX86Fma, out var x86FmaTypeSymbolForMethods))
399+
{
400+
AddTernaryMethods(methodSymbols, "MultiplyAdd", x86FmaTypeSymbolForMethods, RuleKind.FusedMultiplyAdd);
401+
}
402+
395403
if (compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeIntrinsicsX86Sse, out var x86SseTypeSymbolForMethods))
396404
{
397405
AddBinaryOperatorMethods(methodSymbols, "AndNot", x86SseTypeSymbolForMethods, RuleKind.AndNot);
@@ -476,6 +484,25 @@ m.ReturnType is INamedTypeSymbol namedReturnTypeSymbol &&
476484

477485
methodSymbols.AddRange(members.Select((m) => new KeyValuePair<IMethodSymbol, RuleKind>(m, ruleKind)));
478486
}
487+
488+
static void AddTernaryMethods(Dictionary<IMethodSymbol, RuleKind> methodSymbols, string name, INamedTypeSymbol typeSymbol, RuleKind ruleKind, params SpecialType[] supportedTypes)
489+
{
490+
// Looking for a method with 3 operands, where all are of the same type as the generic return type, such as:
491+
// Vector128<float> FusedMultiplyAdd(Vector128<float> a, Vector128<float> b, Vector128<float> c);
492+
493+
IEnumerable<IMethodSymbol> members =
494+
typeSymbol.GetMembers(name)
495+
.OfType<IMethodSymbol>()
496+
.Where((m) => m.Parameters.Length == 3 &&
497+
m.ReturnType is INamedTypeSymbol namedReturnTypeSymbol &&
498+
namedReturnTypeSymbol.Arity == 1 &&
499+
((supportedTypes.Length == 0) || supportedTypes.Contains(namedReturnTypeSymbol.TypeArguments[0].SpecialType)) &&
500+
SymbolEqualityComparer.Default.Equals(m.Parameters[0].Type, namedReturnTypeSymbol) &&
501+
SymbolEqualityComparer.Default.Equals(m.Parameters[1].Type, namedReturnTypeSymbol) &&
502+
SymbolEqualityComparer.Default.Equals(m.Parameters[2].Type, namedReturnTypeSymbol));
503+
504+
methodSymbols.AddRange(members.Select((m) => new KeyValuePair<IMethodSymbol, RuleKind>(m, ruleKind)));
505+
}
479506
}
480507

481508
private void AnalyzeInvocation(OperationAnalysisContext context, Dictionary<IMethodSymbol, RuleKind> methodSymbols)

src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.CodeQuality.Analyzers/Maintainability/UseCrossPlatformIntrinsicsFixer.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ protected virtual SyntaxNode ReplaceNode(SyntaxNode currentNode, SyntaxGenerator
7070
RuleKind.Min => ReplaceWithBinaryMethod(currentNode, generator, "Min"),
7171

7272
RuleKind.ConditionalSelect => ReplaceWithTernaryMethod(currentNode, generator, "ConditionalSelect"),
73+
RuleKind.FusedMultiplyAdd => ReplaceWithTernaryMethod(currentNode, generator, "FusedMultiplyAdd"),
7374

7475
_ => currentNode,
7576
};

src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.CodeQuality.Analyzers/MicrosoftCodeQualityAnalyzersResources.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1374,6 +1374,9 @@
13741374
<data name="UseCrossPlatformIntrinsicsMessage_Floor" xml:space="preserve">
13751375
<value>The cross-platform 'Floor' method should be preferred</value>
13761376
</data>
1377+
<data name="UseCrossPlatformIntrinsicsMessage_FusedMultiplyAdd" xml:space="preserve">
1378+
<value>The cross-platform 'FusedMultiplyAdd' method should be preferred</value>
1379+
</data>
13771380
<data name="UseCrossPlatformIntrinsicsMessage_Max" xml:space="preserve">
13781381
<value>The cross-platform 'Max' method should be preferred</value>
13791382
</data>

src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.cs.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.de.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.es.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.fr.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.it.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.CodeQuality.Analyzers/xlf/MicrosoftCodeQualityAnalyzersResources.ja.xlf

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)