Skip to content

Commit

Permalink
Intrinsics analyzer and fixes (#85481)
Browse files Browse the repository at this point in the history
* 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 analyzer attempts to be conservative about allowed patterns. 

All existing code in System.Private.CoreLib has been annotated to avoid producing
errors.

See the markdown document for details.

Co-authored-by: Jeremy Koritzinsky <jkoritzinsky@gmail.com>
  • Loading branch information
davidwrighton and jkoritzinsky authored May 16, 2023
1 parent 4242567 commit 8a2aec1
Show file tree
Hide file tree
Showing 39 changed files with 2,399 additions and 227 deletions.
201 changes: 88 additions & 113 deletions docs/design/coreclr/botr/vectors-and-intrinsics.md

Large diffs are not rendered by default.

365 changes: 365 additions & 0 deletions src/coreclr/tools/Common/JitInterface/CorInfoInstructionSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit 8a2aec1

Please sign in to comment.