From 4252e77681840228d95318e920d649bb5b27ea82 Mon Sep 17 00:00:00 2001 From: Fei Peng Date: Fri, 15 Sep 2017 13:01:13 -0700 Subject: [PATCH] Implement IsSuppoored for all ISA classes --- src/inc/corinfo.h | 11 +- src/inc/corjit.h | 33 +- src/jit/CMakeLists.txt | 2 + src/jit/compiler.cpp | 66 ++++ src/jit/compiler.h | 35 ++ src/jit/hwintrinsiclistxarch.h | 63 ++++ src/jit/hwintrinsicxarch.cpp | 382 +++++++++++++++++++++ src/jit/importer.cpp | 14 +- src/jit/instr.h | 27 +- src/jit/jit.settings.targets | 2 + src/jit/jitee.h | 45 +++ src/jit/namedintrinsiclist.h | 10 +- src/vm/codeman.cpp | 88 ++++- src/vm/methodtablebuilder.cpp | 13 + .../test_dependencies/test_dependencies.csproj | 3 + tests/src/JIT/HardwareIntrinsics/IsSupported.cs | 89 +++++ .../src/JIT/HardwareIntrinsics/IsSupported.csproj | 33 ++ 17 files changed, 896 insertions(+), 20 deletions(-) create mode 100644 src/jit/hwintrinsiclistxarch.h create mode 100644 src/jit/hwintrinsicxarch.cpp create mode 100644 tests/src/JIT/HardwareIntrinsics/IsSupported.cs create mode 100644 tests/src/JIT/HardwareIntrinsics/IsSupported.csproj diff --git a/src/inc/corinfo.h b/src/inc/corinfo.h index aa024e0..b03f045 100644 --- a/src/inc/corinfo.h +++ b/src/inc/corinfo.h @@ -213,11 +213,12 @@ TODO: Talk about initializing strutures before use #define SELECTANY extern __declspec(selectany) #endif -SELECTANY const GUID JITEEVersionIdentifier = { /* 76a743cd-8a07-471e-9ac4-cd5806a8ffac */ - 0x76a743cd, - 0x8a07, - 0x471e, - {0x9a, 0xc4, 0xcd, 0x58, 0x06, 0xa8, 0xff, 0xac} +// {CFEC7B89-D5FF-4A67-823A-EF99FE0286F4} +SELECTANY const GUID JITEEVersionIdentifier = { + 0xcfec7b89, + 0xd5ff, + 0x4a67, + { 0x82, 0x3a, 0xef, 0x99, 0xfe, 0x2, 0x86, 0xf4 } }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/inc/corjit.h b/src/inc/corjit.h index 39eafe2..6149859 100644 --- a/src/inc/corjit.h +++ b/src/inc/corjit.h @@ -155,7 +155,38 @@ public: CORJIT_FLAG_UNUSED11 = 41, #endif // !defined(_TARGET_ARM_) - CORJIT_FLAG_NO_INLINING = 42 // JIT should not inline any called method into this method + CORJIT_FLAG_NO_INLINING = 42, // JIT should not inline any called method into this method + +#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_) + + CORJIT_FLAG_USE_SSE3 = 43, + CORJIT_FLAG_USE_SSSE3 = 44, + CORJIT_FLAG_USE_SSE41 = 45, + CORJIT_FLAG_USE_SSE42 = 46, + CORJIT_FLAG_USE_AES = 47, + CORJIT_FLAG_USE_BMI1 = 48, + CORJIT_FLAG_USE_BMI2 = 49, + CORJIT_FLAG_USE_FMA = 50, + CORJIT_FLAG_USE_LZCNT = 51, + CORJIT_FLAG_USE_PCLMULQDQ = 52, + CORJIT_FLAG_USE_POPCNT = 53 + + +#else // !defined(_TARGET_X86_) && !defined(_TARGET_AMD64_) + + CORJIT_FLAG_UNUSED12 = 43, + CORJIT_FLAG_UNUSED13 = 44, + CORJIT_FLAG_UNUSED14 = 45, + CORJIT_FLAG_UNUSED15 = 46, + CORJIT_FLAG_UNUSED16 = 47, + CORJIT_FLAG_UNUSED17 = 48, + CORJIT_FLAG_UNUSED18 = 49, + CORJIT_FLAG_UNUSED19 = 50, + CORJIT_FLAG_UNUSED20 = 51, + CORJIT_FLAG_UNUSED21 = 52, + CORJIT_FLAG_UNUSED22 = 53 + +#endif // !defined(_TARGET_X86_) && !defined(_TARGET_AMD64_) }; CORJIT_FLAGS() diff --git a/src/jit/CMakeLists.txt b/src/jit/CMakeLists.txt index 87c47b5..a6efcd8 100644 --- a/src/jit/CMakeLists.txt +++ b/src/jit/CMakeLists.txt @@ -100,6 +100,7 @@ set( JIT_AMD64_SOURCES simdcodegenxarch.cpp targetamd64.cpp unwindamd64.cpp + hwintrinsicxarch.cpp ) set( JIT_ARM_SOURCES @@ -127,6 +128,7 @@ set( JIT_I386_SOURCES simdcodegenxarch.cpp targetx86.cpp unwindx86.cpp + hwintrinsicxarch.cpp ) set( JIT_ARM64_SOURCES diff --git a/src/jit/compiler.cpp b/src/jit/compiler.cpp index 552a6df..86179ec 100644 --- a/src/jit/compiler.cpp +++ b/src/jit/compiler.cpp @@ -2560,6 +2560,72 @@ void Compiler::compSetProcessor() #endif // DEBUG #endif // _TARGET_X86_ + +// Instruction set flags fo// Instruction set flags for Intel hardware intrinsics +#ifdef _TARGET_XARCH_ + opts.compSupportsISA = 0; + + if (!jitFlags.IsSet(JitFlags::JIT_FLAG_PREJIT)) + { + if (opts.compCanUseSSE2) + { + opts.setSupportedISA(InstructionSet_SSE); + opts.setSupportedISA(InstructionSet_SSE2); + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_AES)) + { + opts.setSupportedISA(InstructionSet_AES); + } + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_AVX)) + { + opts.setSupportedISA(InstructionSet_AVX); + } + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_AVX2)) + { + opts.setSupportedISA(InstructionSet_AVX2); + } + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_BMI1)) + { + opts.setSupportedISA(InstructionSet_BMI1); + } + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_BMI2)) + { + opts.setSupportedISA(InstructionSet_BMI2); + } + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_FMA)) + { + opts.setSupportedISA(InstructionSet_FMA); + } + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_LZCNT)) + { + opts.setSupportedISA(InstructionSet_LZCNT); + } + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_PCLMULQDQ)) + { + opts.setSupportedISA(InstructionSet_PCLMULQDQ); + } + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_POPCNT)) + { + opts.setSupportedISA(InstructionSet_POPCNT); + } + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_SSE3)) + { + opts.setSupportedISA(InstructionSet_SSE3); + } + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_SSE41)) + { + opts.setSupportedISA(InstructionSet_SSE41); + } + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_SSE42)) + { + opts.setSupportedISA(InstructionSet_SSE42); + } + if (jitFlags.IsSet(JitFlags::JIT_FLAG_USE_SSSE3)) + { + opts.setSupportedISA(InstructionSet_SSSE3); + } + } + } +#endif } #ifdef PROFILING_SUPPORTED diff --git a/src/jit/compiler.h b/src/jit/compiler.h index 21a2697..dfc50ee 100644 --- a/src/jit/compiler.h +++ b/src/jit/compiler.h @@ -3016,6 +3016,28 @@ protected: CorInfoIntrinsics intrinsicID, bool tailCall); NamedIntrinsic lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method); + +#ifdef _TARGET_XARCH_ + InstructionSet lookupHWIntrinsicISA(const char* className); + NamedIntrinsic lookupHWIntrinsic(const char* methodName, InstructionSet isa); + InstructionSet isaOfHWIntrinsic(NamedIntrinsic intrinsic); + GenTree* impX86HWIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method); + GenTree* impSSEIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method); + GenTree* impSSE2Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method); + GenTree* impSSE3Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method); + GenTree* impSSSE3Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method); + GenTree* impSSE41Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method); + GenTree* impSSE42Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method); + GenTree* impAVXIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method); + GenTree* impAVX2Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method); + GenTree* impAESIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method); + GenTree* impBMI1Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method); + GenTree* impBMI2Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method); + GenTree* impFMAIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method); + GenTree* impLZCNTIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method); + GenTree* impPCLMULQDQIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method); + GenTree* impPOPCNTIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method); +#endif GenTreePtr impArrayAccessIntrinsic(CORINFO_CLASS_HANDLE clsHnd, CORINFO_SIG_INFO* sig, int memberRef, @@ -7915,6 +7937,19 @@ public: #endif // FEATURE_AVX_SUPPORT #endif // _TARGET_XARCH_ +#ifdef _TARGET_XARCH_ + // only for Intel hardware intrinsics + uint64_t compSupportsISA; + void setSupportedISA(InstructionSet isa) + { + compSupportsISA |= 1 << isa; + } + bool compSupports(InstructionSet isa) + { + return (compSupportsISA & (1 << isa)) != 0; + } +#endif + // optimize maximally and/or favor speed over size? #define DEFAULT_MIN_OPTS_CODE_SIZE 60000 diff --git a/src/jit/hwintrinsiclistxarch.h b/src/jit/hwintrinsiclistxarch.h new file mode 100644 index 0000000..d066d10 --- /dev/null +++ b/src/jit/hwintrinsiclistxarch.h @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*****************************************************************************/ +#ifndef HARDWARE_INTRINSIC +#error Define HARDWARE_INTRINSIC before including this file +#endif +/*****************************************************************************/ + +// clang-format off + +#ifdef _TARGET_XARCH_ +// Intrinsic ID Function name ISA +// SSE Intrinsics +HARDWARE_INTRINSIC(SSE_IsSupported, "get_IsSupported", SSE) + +// SSE2 Intrinsics +HARDWARE_INTRINSIC(SSE2_IsSupported, "get_IsSupported", SSE2) + +// SSE3 Intrinsics +HARDWARE_INTRINSIC(SSE3_IsSupported, "get_IsSupported", SSE3) + +// SSSE3 Intrinsics +HARDWARE_INTRINSIC(SSSE3_IsSupported, "get_IsSupported", SSSE3) + +// SSE41 Intrinsics +HARDWARE_INTRINSIC(SSE41_IsSupported, "get_IsSupported", SSE41) + +// SSE42 Intrinsics +HARDWARE_INTRINSIC(SSE42_IsSupported, "get_IsSupported", SSE42) + +// AVX Intrinsics +HARDWARE_INTRINSIC(AVX_IsSupported, "get_IsSupported", AVX) + +// AVX2 Intrinsics +HARDWARE_INTRINSIC(AVX2_IsSupported, "get_IsSupported", AVX2) + +// AES Intrinsics +HARDWARE_INTRINSIC(AES_IsSupported, "get_IsSupported", AES) + +// BMI1 Intrinsics +HARDWARE_INTRINSIC(BMI1_IsSupported, "get_IsSupported", BMI1) + +// BMI2 Intrinsics +HARDWARE_INTRINSIC(BMI2_IsSupported, "get_IsSupported", BMI2) + +// FMA Intrinsics +HARDWARE_INTRINSIC(FMA_IsSupported, "get_IsSupported", FMA) + +// LZCNT Intrinsics +HARDWARE_INTRINSIC(LZCNT_IsSupported, "get_IsSupported", LZCNT) + +// PCLMULQDQ Intrinsics +HARDWARE_INTRINSIC(PCLMULQDQ_IsSupported, "get_IsSupported", PCLMULQDQ) + +// POPCNT Intrinsics +HARDWARE_INTRINSIC(POPCNT_IsSupported, "get_IsSupported", POPCNT) +#endif + +#undef HARDWARE_INTRINSIC + +// clang-format on diff --git a/src/jit/hwintrinsicxarch.cpp b/src/jit/hwintrinsicxarch.cpp new file mode 100644 index 0000000..4a1ca85 --- /dev/null +++ b/src/jit/hwintrinsicxarch.cpp @@ -0,0 +1,382 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "jitpch.h" + +#ifdef _TARGET_XARCH_ + +struct HWIntrinsicInfo +{ + NamedIntrinsic intrinsicID; + const char* intrinsicName; + InstructionSet isa; +} + +static const hwIntrinsicInfoArray[] = { +#define HARDWARE_INTRINSIC(id, name, isa) {NI_##id, name, InstructionSet_##isa}, +#include "hwintrinsiclistxarch.h" +}; + +//------------------------------------------------------------------------ +// lookupHWIntrinsicISA: map class name to InstructionSet value +// +// Arguments: +// className -- class name in System.Runtime.Intrinsics.X86 +// +// Return Value: +// Id for the ISA class. +// +InstructionSet Compiler::lookupHWIntrinsicISA(const char* className) +{ + if (className != nullptr) + { + if (className[0] == 'A') + { + if (strcmp(className, "Aes") == 0) + { + return InstructionSet_AES; + } + else if (strcmp(className, "Avx") == 0) + { + return InstructionSet_AVX; + } + else if (strcmp(className, "Avx2") == 0) + { + return InstructionSet_AVX2; + } + } + if (className[0] == 'S') + { + if (strcmp(className, "Sse") == 0) + { + return InstructionSet_SSE; + } + else if (strcmp(className, "Sse2") == 0) + { + return InstructionSet_SSE2; + } + else if (strcmp(className, "Sse3") == 0) + { + return InstructionSet_SSE3; + } + else if (strcmp(className, "Ssse3") == 0) + { + return InstructionSet_SSSE3; + } + else if (strcmp(className, "Sse41") == 0) + { + return InstructionSet_SSE41; + } + else if (strcmp(className, "Sse42") == 0) + { + return InstructionSet_SSE42; + } + } + + if (strcmp(className, "Bmi1") == 0) + { + return InstructionSet_BMI1; + } + else if (strcmp(className, "Bmi2") == 0) + { + return InstructionSet_BMI2; + } + else if (strcmp(className, "Fma") == 0) + { + return InstructionSet_FMA; + } + else if (strcmp(className, "Lzcnt") == 0) + { + return InstructionSet_LZCNT; + } + else if (strcmp(className, "Pclmulqdq") == 0) + { + return InstructionSet_PCLMULQDQ; + } + else if (strcmp(className, "Popcnt") == 0) + { + return InstructionSet_POPCNT; + } + } + + JITDUMP("Unsupported ISA.\n"); + return InstructionSet_ILLEGAL; +} + +//------------------------------------------------------------------------ +// lookupHWIntrinsic: map intrinsic name to named intrinsic value +// +// Arguments: +// methodName -- name of the intrinsic function. +// isa -- instruction set of the intrinsic. +// +// Return Value: +// Id for the hardware intrinsic. +// +// TODO-Throughput: replace sequential search by binary search +NamedIntrinsic Compiler::lookupHWIntrinsic(const char* methodName, InstructionSet isa) +{ + NamedIntrinsic result = NI_Illegal; + if (isa != InstructionSet_ILLEGAL) + { + for (int i = 0; i < NI_HW_INTRINSIC_END - NI_HW_INTRINSIC_START; i++) + { + if (isa == hwIntrinsicInfoArray[i].isa && strcmp(methodName, hwIntrinsicInfoArray[i].intrinsicName) == 0) + { + result = hwIntrinsicInfoArray[i].intrinsicID; + } + } + } + return result; +} + +//------------------------------------------------------------------------ +// isaOfHWIntrinsic: map named intrinsic value to its instruction set +// +// Arguments: +// intrinsic -- id of the intrinsic function. +// +// Return Value: +// instruction set of the intrinsic. +// +InstructionSet Compiler::isaOfHWIntrinsic(NamedIntrinsic intrinsic) +{ + assert(intrinsic != NI_Illegal); + assert(intrinsic > NI_HW_INTRINSIC_START && intrinsic < NI_HW_INTRINSIC_END); + return hwIntrinsicInfoArray[intrinsic - NI_HW_INTRINSIC_START - 1].isa; +} + +//------------------------------------------------------------------------ +// impX86HWIntrinsic: dispatch hardware intrinsics to their own implementation +// function +// +// Arguments: +// intrinsic -- id of the intrinsic function. +// method -- method handle of the intrinsic function. +// +// Return Value: +// the expanded intrinsic. +// +GenTree* Compiler::impX86HWIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method) +{ + InstructionSet isa = isaOfHWIntrinsic(intrinsic); + switch (isa) + { + case InstructionSet_SSE: + return impSSEIntrinsic(intrinsic, method); + case InstructionSet_SSE2: + return impSSE2Intrinsic(intrinsic, method); + case InstructionSet_SSE3: + return impSSE3Intrinsic(intrinsic, method); + case InstructionSet_SSSE3: + return impSSSE3Intrinsic(intrinsic, method); + case InstructionSet_SSE41: + return impSSE41Intrinsic(intrinsic, method); + case InstructionSet_SSE42: + return impSSE42Intrinsic(intrinsic, method); + case InstructionSet_AVX: + return impAVXIntrinsic(intrinsic, method); + case InstructionSet_AVX2: + return impAVX2Intrinsic(intrinsic, method); + + case InstructionSet_AES: + return impAESIntrinsic(intrinsic, method); + case InstructionSet_BMI1: + return impBMI1Intrinsic(intrinsic, method); + case InstructionSet_BMI2: + return impBMI2Intrinsic(intrinsic, method); + case InstructionSet_FMA: + return impFMAIntrinsic(intrinsic, method); + case InstructionSet_LZCNT: + return impLZCNTIntrinsic(intrinsic, method); + case InstructionSet_PCLMULQDQ: + return impPCLMULQDQIntrinsic(intrinsic, method); + case InstructionSet_POPCNT: + return impPOPCNTIntrinsic(intrinsic, method); + default: + return nullptr; + } +} + +GenTree* Compiler::impSSEIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method) +{ + switch (intrinsic) + { + case NI_SSE_IsSupported: + return gtNewIconNode(opts.compSupports(InstructionSet_SSE)); + + default: + return nullptr; + } +} + +GenTree* Compiler::impSSE2Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method) +{ + switch (intrinsic) + { + case NI_SSE2_IsSupported: + return gtNewIconNode(opts.compSupports(InstructionSet_SSE2)); + + default: + return nullptr; + } +} + +GenTree* Compiler::impSSE3Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method) +{ + switch (intrinsic) + { + case NI_SSE3_IsSupported: + return gtNewIconNode(opts.compSupports(InstructionSet_SSE3)); + + default: + return nullptr; + } +} + +GenTree* Compiler::impSSSE3Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method) +{ + switch (intrinsic) + { + case NI_SSSE3_IsSupported: + return gtNewIconNode(opts.compSupports(InstructionSet_SSSE3)); + + default: + return nullptr; + } +} + +GenTree* Compiler::impSSE41Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method) +{ + switch (intrinsic) + { + case NI_SSE41_IsSupported: + return gtNewIconNode(opts.compSupports(InstructionSet_SSE41)); + + default: + return nullptr; + } +} + +GenTree* Compiler::impSSE42Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method) +{ + switch (intrinsic) + { + case NI_SSE42_IsSupported: + return gtNewIconNode(opts.compSupports(InstructionSet_SSE42)); + + default: + return nullptr; + } +} + +GenTree* Compiler::impAVXIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method) +{ + switch (intrinsic) + { + case NI_AVX_IsSupported: + return gtNewIconNode(opts.compSupports(InstructionSet_AVX)); + + default: + return nullptr; + } +} + +GenTree* Compiler::impAVX2Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method) +{ + switch (intrinsic) + { + case NI_AVX2_IsSupported: + return gtNewIconNode(opts.compSupports(InstructionSet_AVX2)); + + default: + return nullptr; + } +} + +GenTree* Compiler::impAESIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method) +{ + switch (intrinsic) + { + case NI_AES_IsSupported: + return gtNewIconNode(opts.compSupports(InstructionSet_AES)); + + default: + return nullptr; + } +} + +GenTree* Compiler::impBMI1Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method) +{ + switch (intrinsic) + { + case NI_BMI1_IsSupported: + return gtNewIconNode(opts.compSupports(InstructionSet_BMI1)); + + default: + return nullptr; + } +} + +GenTree* Compiler::impBMI2Intrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method) +{ + switch (intrinsic) + { + case NI_BMI2_IsSupported: + return gtNewIconNode(opts.compSupports(InstructionSet_BMI2)); + + default: + return nullptr; + } +} + +GenTree* Compiler::impFMAIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method) +{ + switch (intrinsic) + { + case NI_FMA_IsSupported: + return gtNewIconNode(opts.compSupports(InstructionSet_FMA)); + + default: + return nullptr; + } +} + +GenTree* Compiler::impLZCNTIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method) +{ + switch (intrinsic) + { + case NI_LZCNT_IsSupported: + return gtNewIconNode(opts.compSupports(InstructionSet_LZCNT)); + + default: + return nullptr; + } +} + +GenTree* Compiler::impPCLMULQDQIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method) +{ + switch (intrinsic) + { + case NI_PCLMULQDQ_IsSupported: + return gtNewIconNode(opts.compSupports(InstructionSet_PCLMULQDQ)); + + default: + return nullptr; + } +} + +GenTree* Compiler::impPOPCNTIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method) +{ + switch (intrinsic) + { + case NI_POPCNT_IsSupported: + return gtNewIconNode(opts.compSupports(InstructionSet_POPCNT)); + + default: + return nullptr; + } +} + +#endif // _TARGET_XARCH_ \ No newline at end of file diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp index fcf3ddc..d16dfe3 100644 --- a/src/jit/importer.cpp +++ b/src/jit/importer.cpp @@ -3735,7 +3735,12 @@ GenTreePtr Compiler::impIntrinsic(GenTreePtr newobjThis, { assert(retNode == nullptr); const NamedIntrinsic ni = lookupNamedIntrinsic(method); - +#ifdef _TARGET_XARCH_ + if (ni > NI_HW_INTRINSIC_START && ni < NI_HW_INTRINSIC_END) + { + retNode = impX86HWIntrinsic(ni, method); + } +#endif switch (ni) { case NI_System_Enum_HasFlag: @@ -3958,6 +3963,13 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) } } +#ifdef _TARGET_XARCH_ + if ((namespaceName != nullptr) && strcmp(namespaceName, "System.Runtime.Intrinsics.X86") == 0) + { + InstructionSet isa = lookupHWIntrinsicISA(className); + result = lookupHWIntrinsic(methodName, isa); + } +#endif return result; } diff --git a/src/jit/instr.h b/src/jit/instr.h index 309a2f5..e364fbc 100644 --- a/src/jit/instr.h +++ b/src/jit/instr.h @@ -280,10 +280,29 @@ enum emitAttr : unsigned enum InstructionSet { #ifdef _TARGET_XARCH_ - InstructionSet_SSE2, // SSE2 Instruction set - InstructionSet_SSE3_4, // SSE3, SSSE3, SSE4.1 and SSE4.2 instruction set - InstructionSet_AVX, // AVX2 instruction set - // TODO-Cleaup - This should be named as InstructionSet_AVX2 + // Linear order start + InstructionSet_ILLEGAL = 0, + InstructionSet_SSE = 1, + InstructionSet_SSE2 = 2, + InstructionSet_SSE3 = 3, + InstructionSet_SSSE3 = 4, + InstructionSet_SSE41 = 5, + InstructionSet_SSE42 = 6, + InstructionSet_SSE3_4 = 7, // SSE3, SSSE3, SSE4.1 and SSE4.2 instruction set + InstructionSet_AVX = 8, + InstructionSet_AVX2 = 9, + // Linear order end + // Instruction sets have the linear order only in above area. + // Values of InstructionSet not in this area cannot be compared + // (i.e. compiler->getSIMDInstructionSet() >= InstructionSet_SSE3_4). + + InstructionSet_AES = 32, + InstructionSet_BMI1 = 33, + InstructionSet_BMI2 = 34, + InstructionSet_FMA = 35, + InstructionSet_LZCNT = 36, + InstructionSet_PCLMULQDQ = 37, + InstructionSet_POPCNT = 38, #elif defined(_TARGET_ARM_) InstructionSet_NEON, #endif diff --git a/src/jit/jit.settings.targets b/src/jit/jit.settings.targets index bde63955..ee1df74 100644 --- a/src/jit/jit.settings.targets +++ b/src/jit/jit.settings.targets @@ -103,6 +103,7 @@ + @@ -114,6 +115,7 @@ + diff --git a/src/jit/jitee.h b/src/jit/jitee.h index 5fc2c2c..4c2359a 100644 --- a/src/jit/jitee.h +++ b/src/jit/jitee.h @@ -88,6 +88,37 @@ public: #endif // !defined(_TARGET_ARM_) JIT_FLAG_NO_INLINING = 42, // JIT should not inline any called method into this method + +#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_) + + JIT_FLAG_USE_SSE3 = 43, + JIT_FLAG_USE_SSSE3 = 44, + JIT_FLAG_USE_SSE41 = 45, + JIT_FLAG_USE_SSE42 = 46, + JIT_FLAG_USE_AES = 47, + JIT_FLAG_USE_BMI1 = 48, + JIT_FLAG_USE_BMI2 = 49, + JIT_FLAG_USE_FMA = 50, + JIT_FLAG_USE_LZCNT = 51, + JIT_FLAG_USE_PCLMULQDQ = 52, + JIT_FLAG_USE_POPCNT = 53 + + +#else // !defined(_TARGET_X86_) && !defined(_TARGET_AMD64_) + + JIT_FLAG_UNUSED12 = 43, + JIT_FLAG_UNUSED13 = 44, + JIT_FLAG_UNUSED14 = 45, + JIT_FLAG_UNUSED15 = 46, + JIT_FLAG_UNUSED16 = 47, + JIT_FLAG_UNUSED17 = 48, + JIT_FLAG_UNUSED18 = 49, + JIT_FLAG_UNUSED19 = 50, + JIT_FLAG_UNUSED20 = 51, + JIT_FLAG_UNUSED21 = 52, + JIT_FLAG_UNUSED22 = 53 + +#endif // !defined(_TARGET_X86_) && !defined(_TARGET_AMD64_) }; // clang-format on @@ -208,6 +239,20 @@ public: FLAGS_EQUAL(CORJIT_FLAGS::CORJIT_FLAG_NO_INLINING, JIT_FLAG_NO_INLINING); +#if defined(_TARGET_X86_) || defined(_TARGET_AMD64_) + FLAGS_EQUAL(CORJIT_FLAGS::CORJIT_FLAG_USE_SSE3, JIT_FLAG_USE_SSE3); + FLAGS_EQUAL(CORJIT_FLAGS::CORJIT_FLAG_USE_SSSE3, JIT_FLAG_USE_SSSE3); + FLAGS_EQUAL(CORJIT_FLAGS::CORJIT_FLAG_USE_SSE41, JIT_FLAG_USE_SSE41); + FLAGS_EQUAL(CORJIT_FLAGS::CORJIT_FLAG_USE_SSE42, JIT_FLAG_USE_SSE42); + FLAGS_EQUAL(CORJIT_FLAGS::CORJIT_FLAG_USE_AES, JIT_FLAG_USE_AES); + FLAGS_EQUAL(CORJIT_FLAGS::CORJIT_FLAG_USE_BMI1, JIT_FLAG_USE_BMI1); + FLAGS_EQUAL(CORJIT_FLAGS::CORJIT_FLAG_USE_BMI2, JIT_FLAG_USE_BMI2); + FLAGS_EQUAL(CORJIT_FLAGS::CORJIT_FLAG_USE_FMA, JIT_FLAG_USE_FMA); + FLAGS_EQUAL(CORJIT_FLAGS::CORJIT_FLAG_USE_LZCNT, JIT_FLAG_USE_LZCNT); + FLAGS_EQUAL(CORJIT_FLAGS::CORJIT_FLAG_USE_PCLMULQDQ, JIT_FLAG_USE_PCLMULQDQ); + FLAGS_EQUAL(CORJIT_FLAGS::CORJIT_FLAG_USE_POPCNT, JIT_FLAG_USE_POPCNT); +#endif // _TARGET_X86_ || _TARGET_AMD64_ + #undef FLAGS_EQUAL } diff --git a/src/jit/namedintrinsiclist.h b/src/jit/namedintrinsiclist.h index fa75cc2..42786a0 100644 --- a/src/jit/namedintrinsiclist.h +++ b/src/jit/namedintrinsiclist.h @@ -7,13 +7,19 @@ // Named jit intrinsics -enum NamedIntrinsic +enum NamedIntrinsic : unsigned int { NI_Illegal = 0, NI_System_Enum_HasFlag = 1, NI_MathF_Round = 2, NI_Math_Round = 3, - NI_System_Collections_Generic_EqualityComparer_get_Default = 4 + NI_System_Collections_Generic_EqualityComparer_get_Default = 4, +#ifdef _TARGET_XARCH_ + NI_HW_INTRINSIC_START, +#define HARDWARE_INTRINSIC(id, name, isa) NI_##id, +#include "hwintrinsiclistxarch.h" + NI_HW_INTRINSIC_END +#endif }; #endif // _NAMEDINTRINSICLIST_H_ diff --git a/src/vm/codeman.cpp b/src/vm/codeman.cpp index 7d90ce9..878119c 100644 --- a/src/vm/codeman.cpp +++ b/src/vm/codeman.cpp @@ -1313,6 +1313,24 @@ void EEJitManager::SetCpuInfo() // It returns the resulting eax in buffer[0-3], ebx in buffer[4-7], ecx in buffer[8-11], // and edx in buffer[12-15]. // We will set the following flags: + // CORJIT_FLAG_USE_SSE3 if the following feature bits are set (input EAX of 1) + // SSE3 - ECX bit 0 (buffer[8] & 0x01) + // CORJIT_FLAG_USE_SSSE3 if the following feature bits are set (input EAX of 1) + // SSE3 - ECX bit 0 (buffer[8] & 0x01) + // SSSE3 - ECX bit 9 (buffer[9] & 0x02) + // CORJIT_FLAG_USE_SSE41 if the following feature bits are set (input EAX of 1) + // SSE3 - ECX bit 0 (buffer[8] & 0x01) + // SSSE3 - ECX bit 9 (buffer[9] & 0x02) + // SSE4.1 - ECX bit 19 (buffer[10] & 0x08) + // CORJIT_FLAG_USE_SSE42 if the following feature bits are set (input EAX of 1) + // SSE3 - ECX bit 0 (buffer[8] & 0x01) + // SSSE3 - ECX bit 9 (buffer[9] & 0x02) + // SSE4.2 - ECX bit 20 (buffer[10] & 0x10) + // CORJIT_FLAG_USE_POPCNT if the following feature bits are set (input EAX of 1) + // SSE3 - ECX bit 0 (buffer[8] & 0x01) + // SSSE3 - ECX bit 9 (buffer[9] & 0x02) + // SSE4.2 - ECX bit 20 (buffer[10] & 0x10) + // POPCNT - ECX bit 23 (buffer[10] & 0x80) // CORJIT_FLAG_USE_SSE3_4 if the following feature bits are set (input EAX of 1) // SSE3 - ECX bit 0 (buffer[8] & 0x01) // SSSE3 - ECX bit 9 (buffer[9] & 0x02) @@ -1321,9 +1339,17 @@ void EEJitManager::SetCpuInfo() // CORJIT_FLAG_USE_AVX if the following feature bits are set (input EAX of 1), and xmmYmmStateSupport returns 1: // OSXSAVE - ECX bit 27 (buffer[11] & 0x08) // AVX - ECX bit 28 (buffer[11] & 0x10) + // CORJIT_FLAG_USE_FMA if the following feature bits are set (input EAX of 1), and xmmYmmStateSupport returns 1: + // FMA - ECX bit 12 (buffer[9] & 0x10) // CORJIT_FLAG_USE_AVX2 if the following feature bit is set (input EAX of 0x07 and input ECX of 0): // AVX2 - EBX bit 5 (buffer[4] & 0x20) // CORJIT_FLAG_USE_AVX_512 is not currently set, but defined so that it can be used in future without + // CORJIT_FLAG_USE_BMI1 if the following feature bit is set (input EAX of 0x07 and input ECX of 0): + // BMI1 - EBX bit 3 (buffer[4] & 0x08) + // CORJIT_FLAG_USE_BMI2 if the following feature bit is set (input EAX of 0x07 and input ECX of 0): + // BMI2 - EBX bit 8 (buffer[5] & 0x01) + // CORJIT_FLAG_USE_LZCNT if the following feature bits are set (input EAX of 80000001H) + // LZCNT - ECX bit 5 (buffer[8] & 0x20) // synchronously updating VM and JIT. (void) getcpuid(1, buffer); // If SSE2 is not enabled, there is no point in checking the rest. @@ -1331,24 +1357,56 @@ void EEJitManager::SetCpuInfo() // TODO: Determine whether we should break out the various SSE options further. if ((buffer[15] & 0x04) != 0) // SSE2 { - if (((buffer[8] & 0x01) != 0) && // SSE3 - ((buffer[9] & 0x02) != 0) && // SSSE3 - ((buffer[10] & 0x08) != 0) && // SSE4.1 - ((buffer[10] & 0x10) != 0)) // SSE4.2 + if ((buffer[8] & 0x01) != 0) // SSE3 + { + CPUCompileFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_USE_SSE3); + if ((buffer[9] & 0x02) != 0) // SSSE3 + { + CPUCompileFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_USE_SSSE3); + if ((buffer[10] & 0x08) != 0) // SSE4.1 + { + CPUCompileFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_USE_SSE41); + } + if ((buffer[10] & 0x10) != 0) // SSE4.2 + { + CPUCompileFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_USE_SSE42); + if ((buffer[10] & 0x80) != 0) // POPCNT + { + CPUCompileFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_USE_POPCNT); + } + } + + if (((buffer[10] & 0x08) != 0) && // SSE4.1 + ((buffer[10] & 0x10) != 0)) // SSE4.2 + { + CPUCompileFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_USE_SSE3_4); + } + } + } + + if ((buffer[11] & 0x01) != 0) // AESNI { - CPUCompileFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_USE_SSE3_4); + CPUCompileFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_USE_AES); } - if ((buffer[11] & 0x18) == 0x18) + if ((buffer[8] & 0x02) != 0) // PCLMULQDQ + { + CPUCompileFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_USE_PCLMULQDQ); + } + if ((buffer[11] & 0x18) == 0x18) // AVX { if(DoesOSSupportAVX()) { if (xmmYmmStateSupport() == 1) { CPUCompileFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_USE_AVX); + if ((buffer[9] & 0x10) != 0) // FMA + { + CPUCompileFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_USE_FMA); + } if (maxCpuId >= 0x07) { (void) getextcpuid(0, 0x07, buffer); - if ((buffer[4] & 0x20) != 0) + if ((buffer[4] & 0x20) != 0) // AVX2 { CPUCompileFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_USE_AVX2); } @@ -1356,6 +1414,22 @@ void EEJitManager::SetCpuInfo() } } } + (void) getextcpuid(0, 0x07, buffer); + if ((buffer[4] & 0x08) != 0) // BMI1 + { + CPUCompileFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_USE_BMI1); + } + if ((buffer[5] & 0x01) != 0) //BMI2 + { + CPUCompileFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_USE_BMI2); + } + + (void) getcpuid(0x80000001, buffer); + if ((buffer[8] & 0x20) != 0) // LZCNT + { + CPUCompileFlags.Set(CORJIT_FLAGS::CORJIT_FLAG_USE_LZCNT); + } + static ConfigDWORD fFeatureSIMD; if (fFeatureSIMD.val(CLRConfig::EXTERNAL_FeatureSIMD) != 0) { diff --git a/src/vm/methodtablebuilder.cpp b/src/vm/methodtablebuilder.cpp index cdee2d8..8a96e68 100644 --- a/src/vm/methodtablebuilder.cpp +++ b/src/vm/methodtablebuilder.cpp @@ -5094,6 +5094,19 @@ MethodTableBuilder::InitNewMethodDesc( { pNewMD->SetIsJitIntrinsic(); } + + // All the funtions in System.Runtime.Intrinsics.X86 are hardware intrinsics. + // We specially treat them here to reduce the disk footprint of mscorlib. + LPCUTF8 className; + LPCUTF8 nameSpace; + + HRESULT hrns = GetMDImport()->GetNameOfTypeDef(bmtInternal->pType->GetTypeDefToken(), &className, &nameSpace); + + if (hrns == S_OK && strcmp(nameSpace, "System.Runtime.Intrinsics.X86") == 0) + { + pNewMD->SetIsJitIntrinsic(); + } + } pNewMD->SetSlot(pMethod->GetSlotIndex()); diff --git a/tests/src/Common/test_dependencies/test_dependencies.csproj b/tests/src/Common/test_dependencies/test_dependencies.csproj index 9eefab2..e9d78ec 100644 --- a/tests/src/Common/test_dependencies/test_dependencies.csproj +++ b/tests/src/Common/test_dependencies/test_dependencies.csproj @@ -22,6 +22,9 @@ $(CoreFxPackageVersion) + + 4.5.0-preview1-25718-03 + netcoreapp2.1 diff --git a/tests/src/JIT/HardwareIntrinsics/IsSupported.cs b/tests/src/JIT/HardwareIntrinsics/IsSupported.cs new file mode 100644 index 0000000..9f6d924 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/IsSupported.cs @@ -0,0 +1,89 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.Intrinsics.X86; + +namespace IntelHardwareIntrinsicTest +{ + class Program + { + static int Main(string[] args) + { + bool result = true; + if (Avx2.IsSupported) + { + if (Avx.IsSupported) + { + if (!Sse42.IsSupported) + { + result = false; + } + + if (Sse41.IsSupported) + { + if (Ssse3.IsSupported) + { + if (Sse3.IsSupported) + { + if (Sse2.IsSupported && Sse.IsSupported) + { + result = result && true; + } + else + { + result = false; + } + } + else + { + result = false; + } + } + else + { + result = false; + } + } + else + { + result = false; + } + } + if (Aes.IsSupported && Bmi1.IsSupported && Bmi2.IsSupported && Fma.IsSupported && + Lzcnt.IsSupported && Popcnt.IsSupported && Pclmulqdq.IsSupported) + { + result = result && true; + } + else + { + result = false; + } + } + + // Non-X86 platforms + if (!(Sse.IsSupported)) + { + if (Sse2.IsSupported || + Sse3.IsSupported || + Ssse3.IsSupported || + Sse41.IsSupported || + Sse42.IsSupported || + Avx.IsSupported || + Avx2.IsSupported || + Aes.IsSupported || + Bmi1.IsSupported || + Bmi2.IsSupported || + Fma.IsSupported || + Lzcnt.IsSupported || + Popcnt.IsSupported|| + Pclmulqdq.IsSupported) + { + result = false; + } + } + return result ? 100 : 0; + } + + } +} \ No newline at end of file diff --git a/tests/src/JIT/HardwareIntrinsics/IsSupported.csproj b/tests/src/JIT/HardwareIntrinsics/IsSupported.csproj new file mode 100644 index 0000000..e8f88b5 --- /dev/null +++ b/tests/src/JIT/HardwareIntrinsics/IsSupported.csproj @@ -0,0 +1,33 @@ + + + + + Debug + AnyCPU + 2.0 + {95DFC527-4DC1-495E-97D7-E94EE1F7140D} + Exe + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + + + + + + + False + + + + None + True + + + + + + + + + + \ No newline at end of file -- 2.7.4