diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 6fadbfa9aede89..8b0f85163825ad 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -941,12 +941,11 @@ class CodeGen final : public CodeGenInterface GenTreeHWIntrinsic* node, instruction ins, emitAttr attr, int8_t ival, insOpts instOptions); void genBaseIntrinsic(GenTreeHWIntrinsic* node, insOpts instOptions); - void genX86BaseIntrinsic(GenTreeHWIntrinsic* node, insOpts instOptions); + void genHWIntrinsicSpecial(GenTreeHWIntrinsic* node, insOpts instOptions); void genAvxFamilyIntrinsic(GenTreeHWIntrinsic* node, insOpts instOptions); void genFmaIntrinsic(GenTreeHWIntrinsic* node, insOpts instOptions); void genPermuteVar2x(GenTreeHWIntrinsic* node, insOpts instOptions); void genXCNTIntrinsic(GenTreeHWIntrinsic* node, instruction ins); - void genX86SerializeIntrinsic(GenTreeHWIntrinsic* node); template void genHWIntrinsicJumpTableFallback(NamedIntrinsic intrinsic, diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index d8cdd203ee8a2c..859690b14a9894 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -8040,7 +8040,7 @@ void emitter::emitIns_R_R_I( void emitter::emitIns_AR(instruction ins, emitAttr attr, regNumber base, int offs, insOpts instOptions) { assert(ins == INS_prefetcht0 || ins == INS_prefetcht1 || ins == INS_prefetcht2 || ins == INS_prefetchnta || - ins == INS_inc || ins == INS_dec); + ins == INS_inc || ins == INS_dec || ins == INS_umonitor); instrDesc* id = emitNewInstrAmd(attr, offs); diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp index ca6a2a7eb223cb..c42d52c1a4c0ee 100644 --- a/src/coreclr/jit/fgdiagnostic.cpp +++ b/src/coreclr/jit/fgdiagnostic.cpp @@ -3405,6 +3405,9 @@ void Compiler::fgDebugCheckFlags(GenTree* tree, BasicBlock* block) case NI_X86Base_Prefetch1: case NI_X86Base_Prefetch2: case NI_X86Base_PrefetchNonTemporal: + case NI_WAITPKG_SetUpUserLevelMonitor: + case NI_WAITPKG_TimedPause: + case NI_WAITPKG_WaitForUserLevelMonitor: { assert(tree->OperRequiresCallFlag(this)); expectedFlags |= GTF_GLOB_REF; diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 10bde780a8534f..6c35e064d28034 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -28579,6 +28579,9 @@ bool GenTreeHWIntrinsic::OperRequiresCallFlag() const case NI_X86Base_Prefetch1: case NI_X86Base_Prefetch2: case NI_X86Base_PrefetchNonTemporal: + case NI_WAITPKG_SetUpUserLevelMonitor: + case NI_WAITPKG_TimedPause: + case NI_WAITPKG_WaitForUserLevelMonitor: { return true; } @@ -28784,6 +28787,9 @@ void GenTreeHWIntrinsic::Initialize(NamedIntrinsic intrinsicId) case NI_X86Base_Prefetch1: case NI_X86Base_Prefetch2: case NI_X86Base_PrefetchNonTemporal: + case NI_WAITPKG_SetUpUserLevelMonitor: + case NI_WAITPKG_TimedPause: + case NI_WAITPKG_WaitForUserLevelMonitor: { // Mark as a call and global reference, much as is done for GT_KEEPALIVE gtFlags |= (GTF_CALL | GTF_GLOB_REF); diff --git a/src/coreclr/jit/hwintrinsic.cpp b/src/coreclr/jit/hwintrinsic.cpp index ca815d3c14a8b6..883613d0ecf133 100644 --- a/src/coreclr/jit/hwintrinsic.cpp +++ b/src/coreclr/jit/hwintrinsic.cpp @@ -960,7 +960,7 @@ static const HWIntrinsicIsaRange hwintrinsicIsaRangeArray[] = { { FIRST_NI_GFNI_V256, LAST_NI_GFNI_V256 }, // GFNI_V256 { FIRST_NI_GFNI_V512, LAST_NI_GFNI_V512 }, // GFNI_V512 { NI_Illegal, NI_Illegal }, // SHA - { NI_Illegal, NI_Illegal }, // WAITPKG + { FIRST_NI_WAITPKG, LAST_NI_WAITPKG }, // WAITPKG { FIRST_NI_X86Serialize, LAST_NI_X86Serialize }, // X86Serialize { FIRST_NI_Vector128, LAST_NI_Vector128 }, // Vector128 { FIRST_NI_Vector256, LAST_NI_Vector256 }, // Vector256 diff --git a/src/coreclr/jit/hwintrinsiccodegenxarch.cpp b/src/coreclr/jit/hwintrinsiccodegenxarch.cpp index 1057660e6ae287..d9a56159d08e54 100644 --- a/src/coreclr/jit/hwintrinsiccodegenxarch.cpp +++ b/src/coreclr/jit/hwintrinsiccodegenxarch.cpp @@ -998,10 +998,12 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) break; } + case InstructionSet_WAITPKG: case InstructionSet_X86Base: case InstructionSet_X86Base_X64: + case InstructionSet_X86Serialize: { - genX86BaseIntrinsic(node, instOptions); + genHWIntrinsicSpecial(node, instOptions); break; } @@ -1018,14 +1020,6 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) break; } - case InstructionSet_X86Serialize: - case InstructionSet_X86Serialize_X64: - { - assert(instOptions == INS_OPTS_NONE); - genX86SerializeIntrinsic(node); - break; - } - default: unreached(); break; @@ -2378,12 +2372,12 @@ void CodeGen::genBaseIntrinsic(GenTreeHWIntrinsic* node, insOpts instOptions) } //------------------------------------------------------------------------ -// genX86BaseIntrinsic: Generates the code for an X86 base hardware intrinsic node +// genHWIntrinsicSpecial: Generates the code for an special hardware intrinsic node // // Arguments: // node - The hardware intrinsic node // -void CodeGen::genX86BaseIntrinsic(GenTreeHWIntrinsic* node, insOpts instOptions) +void CodeGen::genHWIntrinsicSpecial(GenTreeHWIntrinsic* node, insOpts instOptions) { NamedIntrinsic intrinsicId = node->GetHWIntrinsicId(); regNumber targetReg = node->GetRegNum(); @@ -2466,6 +2460,7 @@ void CodeGen::genX86BaseIntrinsic(GenTreeHWIntrinsic* node, insOpts instOptions) case NI_X86Base_Prefetch1: case NI_X86Base_Prefetch2: case NI_X86Base_PrefetchNonTemporal: + case NI_WAITPKG_SetUpUserLevelMonitor: { assert(baseType == TYP_UBYTE); assert(instOptions == INS_OPTS_NONE); @@ -2534,6 +2529,14 @@ void CodeGen::genX86BaseIntrinsic(GenTreeHWIntrinsic* node, insOpts instOptions) break; } + case NI_X86Serialize_Serialize: + { + assert(instOptions == INS_OPTS_NONE); + assert(node->GetSimdBaseType() == TYP_UNKNOWN); + GetEmitter()->emitIns(INS_serialize); + break; + } + case NI_X86Base_ConvertToVector128Int16: case NI_X86Base_ConvertToVector128Int32: case NI_X86Base_ConvertToVector128Int64: @@ -2652,6 +2655,42 @@ void CodeGen::genX86BaseIntrinsic(GenTreeHWIntrinsic* node, insOpts instOptions) break; } + case NI_WAITPKG_TimedPause: + case NI_WAITPKG_WaitForUserLevelMonitor: + { + assert(node->GetSimdBaseType() == TYP_INT); + + assert(node->GetOperandCount() == 3); + assert(instOptions == INS_OPTS_NONE); + + GenTree* op1 = node->Op(1); + GenTree* op2 = node->Op(2); + GenTree* op3 = node->Op(3); + + instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, TYP_INT, compiler); + + regNumber op1Reg = op1->GetRegNum(); + regNumber op2Reg = op2->GetRegNum(); + regNumber op3Reg = op3->GetRegNum(); + + emitAttr attr = emitTypeSize(TYP_INT); + + // op1: free, op2: EDX, op3: EAX + + assert(op1Reg != REG_EDX); + assert(op1Reg != REG_EAX); + + assert(op2Reg != REG_EAX); + assert(op3Reg != REG_EDX); + + emit->emitIns_Mov(INS_mov, attr, REG_EDX, op2Reg, /* canSkip */ true); + emit->emitIns_Mov(INS_mov, attr, REG_EAX, op3Reg, /* canSkip */ true); + + // emit the TPAUSE/UMWAIT instruction + emit->emitIns_R(ins, attr, op1Reg); + break; + } + default: unreached(); break; @@ -3902,33 +3941,4 @@ void CodeGen::genXCNTIntrinsic(GenTreeHWIntrinsic* node, instruction ins) genHWIntrinsic_R_RM(node, ins, emitTypeSize(node->TypeGet()), targetReg, op1, INS_OPTS_NONE); } -//------------------------------------------------------------------------ -// genX86SerializeIntrinsic: Generates the code for an X86 serialize hardware intrinsic node -// -// Arguments: -// node - The hardware intrinsic node -// -void CodeGen::genX86SerializeIntrinsic(GenTreeHWIntrinsic* node) -{ - NamedIntrinsic intrinsicId = node->GetHWIntrinsicId(); - - genConsumeMultiOpOperands(node); - - switch (intrinsicId) - { - case NI_X86Serialize_Serialize: - { - assert(node->GetSimdBaseType() == TYP_UNKNOWN); - GetEmitter()->emitIns(INS_serialize); - break; - } - - default: - unreached(); - break; - } - - genProduceReg(node); -} - #endif // FEATURE_HW_INTRINSICS diff --git a/src/coreclr/jit/hwintrinsiclistxarch.h b/src/coreclr/jit/hwintrinsiclistxarch.h index 482035112b0706..aa5e80884b1eca 100644 --- a/src/coreclr/jit/hwintrinsiclistxarch.h +++ b/src/coreclr/jit/hwintrinsiclistxarch.h @@ -1158,7 +1158,7 @@ HARDWARE_INTRINSIC(AES_V512, CarrylessMultiply, // ISA Function name SIMD size NumArg Instructions Category Flags // {TYP_BYTE, TYP_UBYTE, TYP_SHORT, TYP_USHORT, TYP_INT, TYP_UINT, TYP_LONG, TYP_ULONG, TYP_FLOAT, TYP_DOUBLE} // *************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** -// AVX512 Intrinsics for X86Serialize +// Intrinsics for X86Serialize #define FIRST_NI_X86Serialize NI_X86Serialize_Serialize HARDWARE_INTRINSIC(X86Serialize, Serialize, 0, 0, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Special, HW_Flag_NoContainment|HW_Flag_NoRMWSemantics|HW_Flag_SpecialSideEffect_Barrier) #define LAST_NI_X86Serialize NI_X86Serialize_Serialize @@ -1196,6 +1196,17 @@ HARDWARE_INTRINSIC(GFNI_V512, GaloisFieldAffineTransformInverse, HARDWARE_INTRINSIC(GFNI_V512, GaloisFieldMultiply, 64, 2, {INS_invalid, INS_gf2p8mulb, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_SimpleSIMD, HW_Flag_NoFlag) #define LAST_NI_GFNI_V512 NI_GFNI_V512_GaloisFieldMultiply +// *************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** +// ISA Function name SIMD size NumArg Instructions Category Flags +// {TYP_BYTE, TYP_UBYTE, TYP_SHORT, TYP_USHORT, TYP_INT, TYP_UINT, TYP_LONG, TYP_ULONG, TYP_FLOAT, TYP_DOUBLE} +// *************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** +// Intrinsics for WaitPkg +#define FIRST_NI_WAITPKG NI_WAITPKG_SetUpUserLevelMonitor +HARDWARE_INTRINSIC(WAITPKG, SetUpUserLevelMonitor, 0, 1, {INS_invalid, INS_umonitor, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Special, HW_Flag_NoContainment|HW_Flag_NoRMWSemantics|HW_Flag_SpecialSideEffect_Other) +HARDWARE_INTRINSIC(WAITPKG, TimedPause, 0, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_tpause, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Special, HW_Flag_NoContainment|HW_Flag_NoRMWSemantics|HW_Flag_SpecialSideEffect_Other) +HARDWARE_INTRINSIC(WAITPKG, WaitForUserLevelMonitor, 0, -1, {INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_umwait, INS_invalid, INS_invalid, INS_invalid, INS_invalid, INS_invalid}, HW_Category_Special, HW_Flag_NoContainment|HW_Flag_NoRMWSemantics|HW_Flag_SpecialSideEffect_Other) +#define LAST_NI_WAITPKG NI_WAITPKG_WaitForUserLevelMonitor + // *************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** // ISA Function name SIMD size NumArg Instructions Category Flags // {TYP_BYTE, TYP_UBYTE, TYP_SHORT, TYP_USHORT, TYP_INT, TYP_UINT, TYP_LONG, TYP_ULONG, TYP_FLOAT, TYP_DOUBLE} diff --git a/src/coreclr/jit/hwintrinsicxarch.cpp b/src/coreclr/jit/hwintrinsicxarch.cpp index ded77bd68d147e..2ae0585320d09c 100644 --- a/src/coreclr/jit/hwintrinsicxarch.cpp +++ b/src/coreclr/jit/hwintrinsicxarch.cpp @@ -4295,6 +4295,7 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, case NI_X86Base_Prefetch1: case NI_X86Base_Prefetch2: case NI_X86Base_PrefetchNonTemporal: + case NI_WAITPKG_SetUpUserLevelMonitor: { assert(sig->numArgs == 1); assert(JITtype2varType(sig->retType) == TYP_VOID); @@ -4335,6 +4336,29 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, break; } + case NI_WAITPKG_TimedPause: + case NI_WAITPKG_WaitForUserLevelMonitor: + { + assert(sig->numArgs == 2); + assert(JITtype2varType(sig->retType) == TYP_UBYTE); + assert(simdSize == 0); + + op2 = impPopStack().val; + op1 = impPopStack().val; + + GenTree* op2Hi = nullptr; + GenTree* op2Lo = nullptr; + + GenTree* op2Dup = fgMakeMultiUse(&op2); + + op2Hi = gtFoldExpr(gtNewOperNode(GT_RSZ, TYP_LONG, op2, gtNewIconNode(32))); + op2Hi = gtFoldExpr(gtNewCastNode(TYP_INT, op2Hi, false, TYP_INT)); + op2Lo = gtFoldExpr(gtNewCastNode(TYP_INT, op2Dup, false, TYP_INT)); + + retNode = gtNewSimdHWIntrinsicNode(retType, op1, op2Hi, op2Lo, intrinsic, CORINFO_TYPE_INT, 0); + break; + } + case NI_AVX2_PermuteVar8x32: case NI_AVX512_PermuteVar4x64: case NI_AVX512_PermuteVar8x16: diff --git a/src/coreclr/jit/instrsxarch.h b/src/coreclr/jit/instrsxarch.h index 5eeb20bb5844c5..6768489edbca59 100644 --- a/src/coreclr/jit/instrsxarch.h +++ b/src/coreclr/jit/instrsxarch.h @@ -1197,9 +1197,9 @@ INST3(popcnt_apx, "popcnt", IUM_WR, BAD_CODE, BAD_CODE, #endif // WAITPKG -INST3(tpause, "tpause", IUM_RD, BAD_CODE, BAD_CODE, PCKDBL(0xAE), ILLEGAL, ILLEGAL, INS_TT_NONE, Resets_OF | Resets_SF | Resets_ZF | Resets_AF | Resets_PF | Writes_CF) // Timed PAUSE -INST3(umonitor, "umonitor", IUM_RD, BAD_CODE, BAD_CODE, SSEFLT(0xAE), ILLEGAL, ILLEGAL, INS_TT_NONE, INS_FLAGS_None) // User Level Set Up Monitor Address -INST3(umwait, "umwait", IUM_RD, BAD_CODE, BAD_CODE, SSEDBL(0xAE), ILLEGAL, ILLEGAL, INS_TT_NONE, Resets_OF | Resets_SF | Resets_ZF | Resets_AF | Resets_PF | Writes_CF) // User Level Monitor Wait +INST3(tpause, "tpause", IUM_RD, PCKDBL(0xAE), BAD_CODE, BAD_CODE, 140C, 1C, INS_TT_NONE, Resets_OF | Resets_SF | Resets_ZF | Resets_AF | Resets_PF | Writes_CF) // Timed PAUSE +INST3(umonitor, "umonitor", IUM_RD, SSEFLT(0xAE), BAD_CODE, BAD_CODE, 140C, 1C, INS_TT_NONE, INS_FLAGS_None) // User Level Set Up Monitor Address +INST3(umwait, "umwait", IUM_RD, SSEDBL(0xAE), BAD_CODE, BAD_CODE, 140C, 1C, INS_TT_NONE, Resets_OF | Resets_SF | Resets_ZF | Resets_AF | Resets_PF | Writes_CF) // User Level Monitor Wait INST3(neg, "neg", IUM_RW, 0x0018F6, BAD_CODE, 0x0018F6, ILLEGAL, ILLEGAL, INS_TT_NONE, Writes_OF | Writes_SF | Writes_ZF | Writes_AF | Writes_PF | Writes_CF | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD | INS_Flags_Has_NF) INST3(not, "not", IUM_RW, 0x0010F6, BAD_CODE, 0x0010F6, ILLEGAL, ILLEGAL, INS_TT_NONE, INS_FLAGS_None | INS_FLAGS_Has_Wbit | Encoding_REX2 | INS_Flags_Has_NDD) diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index 4dac70f9e6998a..ed2e9a4be0be07 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -2615,6 +2615,16 @@ GenTree* Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node) break; } + case NI_WAITPKG_TimedPause: + case NI_WAITPKG_WaitForUserLevelMonitor: + { + GenTree* cc = LowerNodeCC(node, GenCondition::C); + + node->gtType = TYP_VOID; + node->ClearUnusedValue(); + break; + } + default: break; } diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp index 0174e6b3f7f9cf..db1b341cf46775 100644 --- a/src/coreclr/jit/lsraxarch.cpp +++ b/src/coreclr/jit/lsraxarch.cpp @@ -2844,6 +2844,21 @@ int LinearScan::BuildHWIntrinsic(GenTreeHWIntrinsic* intrinsicTree, int* pDstCou break; } + case NI_WAITPKG_TimedPause: + case NI_WAITPKG_WaitForUserLevelMonitor: + { + assert(numArgs == 3); + SingleTypeRegSet apxAwareRegCandidates = + ForceLowGprForApxIfNeeded(op1, RBM_NONE, canHWIntrinsicUseApxRegs); + + srcCount += BuildOperandUses(op1, apxAwareRegCandidates); + srcCount += BuildOperandUses(op2, SRBM_EDX); + srcCount += BuildOperandUses(op3, SRBM_EAX); + + buildUses = false; + break; + } + default: { assert((intrinsicId > NI_HW_INTRINSIC_START) && (intrinsicId < NI_HW_INTRINSIC_END)); diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.NoX86Intrinsics.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.NoX86Intrinsics.xml index 9e3e8a1d6e7201..43b4630acf7387 100644 --- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.NoX86Intrinsics.xml +++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.NoX86Intrinsics.xml @@ -174,6 +174,12 @@ + + + + + + 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 56bb2664ff9c33..226e618fb045a5 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 @@ -2721,6 +2721,7 @@ + @@ -2753,6 +2754,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/WaitPkg.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/WaitPkg.PlatformNotSupported.cs new file mode 100644 index 00000000000000..b6eec88558185e --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/WaitPkg.PlatformNotSupported.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; + +namespace System.Runtime.Intrinsics.X86 +{ + /// Provides access to the x86 WAITPKG hardware instruction via intrinsics. + [CLSCompliant(false)] + public abstract class WaitPkg : X86Base + { + internal WaitPkg() { } + + /// Gets a value that indicates whether the APIs in this class are supported. + /// if the APIs are supported; otherwise, . + /// A value of indicates that the APIs will throw . + public static new bool IsSupported { [Intrinsic] get => false; } + + /// Provides access to the x86 WAITPKG hardware instructions, which are only available to 64-bit processes, via intrinsics. + [Intrinsic] + public new abstract class X64 : X86Base.X64 + { + internal X64() { } + + /// Gets a value that indicates whether the APIs in this class are supported. + /// if the APIs are supported; otherwise, . + /// A value of indicates that the APIs will throw . + public static new bool IsSupported { [Intrinsic] get => false; } + } + + /// + /// void _umonitor(void *address) + /// UMONITOR r64 + /// + public static unsafe void SetUpUserLevelMonitor(void* address) { throw new PlatformNotSupportedException(); } + + /// + /// uint8_t _tpause(uint32_t control, uint64_t counter) + /// TPAUSE r32, <EDX>, <EAX> + /// + public static bool TimedPause(uint control, ulong counter) { throw new PlatformNotSupportedException(); } + + /// + /// uint8_t _umwait(uint32_t control, uint64_t counter) + /// UMWAIT r32, <EDX>, <EAX> + /// + public static bool WaitForUserLevelMonitor(uint control, ulong counter) { throw new PlatformNotSupportedException(); } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/WaitPkg.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/WaitPkg.cs new file mode 100644 index 00000000000000..4f1c4ed0737c6f --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/X86/WaitPkg.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; + +namespace System.Runtime.Intrinsics.X86 +{ + /// Provides access to the x86 WAITPKG hardware instruction via intrinsics. + [Intrinsic] + [CLSCompliant(false)] + public abstract class WaitPkg : X86Base + { + internal WaitPkg() { } + + /// Gets a value that indicates whether the APIs in this class are supported. + /// if the APIs are supported; otherwise, . + /// A value of indicates that the APIs will throw . + public static new bool IsSupported { get => IsSupported; } + + /// Provides access to the x86 WAITPKG hardware instructions, that are only available to 64-bit processes, via intrinsics. + [Intrinsic] + public new abstract class X64 : X86Base.X64 + { + internal X64() { } + + /// Gets a value that indicates whether the APIs in this class are supported. + /// if the APIs are supported; otherwise, . + /// A value of indicates that the APIs will throw . + public static new bool IsSupported { get => IsSupported; } + } + + /// + /// void _umonitor(void *address) + /// UMONITOR r64 + /// + public static unsafe void SetUpUserLevelMonitor(void* address) => SetUpUserLevelMonitor(address); + + /// + /// uint8_t _tpause(uint32_t control, uint64_t counter) + /// TPAUSE r32, <EDX>, <EAX> + /// + public static bool TimedPause(uint control, ulong counter) => TimedPause(control, counter); + + /// + /// uint8_t _umwait(uint32_t control, uint64_t counter) + /// UMWAIT r32, <EDX>, <EAX> + /// + public static bool WaitForUserLevelMonitor(uint control, ulong counter) => WaitForUserLevelMonitor(control, counter); + } +} diff --git a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs index 0775004609bc4b..1487264c5baeb2 100644 --- a/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs +++ b/src/libraries/System.Runtime.Intrinsics/ref/System.Runtime.Intrinsics.cs @@ -10903,7 +10903,20 @@ internal X64() { } public static new bool IsSupported { get { throw null; } } } } - + [System.CLSCompliantAttribute(false)] + public abstract partial class WaitPkg : System.Runtime.Intrinsics.X86.X86Base + { + internal WaitPkg() { } + public static new bool IsSupported { get { throw null; } } + public static unsafe void SetUpUserLevelMonitor(void* address) { throw null; } + public static bool TimedPause(uint control, ulong counter) { throw null; } + public static bool WaitForUserLevelMonitor(uint control, ulong counter) { throw null; } + public new abstract partial class X64 : System.Runtime.Intrinsics.X86.X86Base.X64 + { + internal X64() { } + public static new bool IsSupported { get { throw null; } } + } + } [System.CLSCompliantAttribute(false)] public abstract partial class X86Base { diff --git a/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Intrinsics.x86.xml b/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Intrinsics.x86.xml index 1e99c7fba5fefe..307cf715d8d893 100644 --- a/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Intrinsics.x86.xml +++ b/src/mono/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Intrinsics.x86.xml @@ -99,6 +99,12 @@ + + + + + + diff --git a/src/mono/mono/mini/simd-intrinsics.c b/src/mono/mono/mini/simd-intrinsics.c index ebe0637605fe5a..8a391449f3e282 100644 --- a/src/mono/mono/mini/simd-intrinsics.c +++ b/src/mono/mono/mini/simd-intrinsics.c @@ -5715,6 +5715,7 @@ static const IntrinGroup supported_x86_intrinsics [] = { { "Sse41", MONO_CPU_X86_SSE41, sse41_methods, sizeof (sse41_methods) }, { "Sse42", MONO_CPU_X86_SSE42, sse42_methods, sizeof (sse42_methods) }, { "Ssse3", MONO_CPU_X86_SSSE3, ssse3_methods, sizeof (ssse3_methods) }, + { "WaitPkg", 0, unsupported, sizeof (unsupported) }, { "X86Base", MONO_CPU_INITED, x86base_methods, sizeof (x86base_methods), TRUE }, { "X86Serialize", 0, unsupported, sizeof (unsupported) }, }; diff --git a/src/tests/JIT/HardwareIntrinsics/X86/General/IsSupported.cs b/src/tests/JIT/HardwareIntrinsics/X86/General/IsSupported.cs index d49d86031a37da..410ba82dfe4d6e 100644 --- a/src/tests/JIT/HardwareIntrinsics/X86/General/IsSupported.cs +++ b/src/tests/JIT/HardwareIntrinsics/X86/General/IsSupported.cs @@ -90,6 +90,8 @@ public static void IsSupported() Convert.ToBoolean(typeof(Pclmulqdq.V256).GetMethod(issupported).Invoke(null, null)) != Pclmulqdq.V256.IsSupported || Convert.ToBoolean(typeof(Pclmulqdq.V512).GetMethod(issupported).Invoke(null, null)) != Pclmulqdq.V512.IsSupported || Convert.ToBoolean(typeof(Pclmulqdq.X64).GetMethod(issupported).Invoke(null, null)) != Pclmulqdq.X64.IsSupported || + Convert.ToBoolean(typeof(WaitPkg).GetMethod(issupported).Invoke(null, null)) != WaitPkg.IsSupported || + Convert.ToBoolean(typeof(WaitPkg.X64).GetMethod(issupported).Invoke(null, null)) != WaitPkg.X64.IsSupported || Convert.ToBoolean(typeof(X86Base).GetMethod(issupported).Invoke(null, null)) != X86Base.IsSupported || Convert.ToBoolean(typeof(X86Base.X64).GetMethod(issupported).Invoke(null, null)) != X86Base.X64.IsSupported || Convert.ToBoolean(typeof(X86Serialize).GetMethod(issupported).Invoke(null, null)) != X86Serialize.IsSupported || diff --git a/src/tests/JIT/HardwareIntrinsics/X86/WaitPkg/WaitPkg.cs b/src/tests/JIT/HardwareIntrinsics/X86/WaitPkg/WaitPkg.cs new file mode 100644 index 00000000000000..335bc01f362b81 --- /dev/null +++ b/src/tests/JIT/HardwareIntrinsics/X86/WaitPkg/WaitPkg.cs @@ -0,0 +1,62 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +using Xunit; + +namespace IntelHardwareIntrinsicTest +{ + public class Program + { + [Fact] + public static unsafe void TestUserLevelMonitor() + { + if (WaitPkg.IsSupported) + { + RunBasicScenario(); + } + else + { + Assert.Throws(RunBasicScenario); + } + + static void RunBasicScenario() + { + // We're really just testing this doesn't throw. So + // indicate a fast wakeup and the lowest counter possible + + byte* data = stackalloc byte[1]; + + WaitPkg.SetUpUserLevelMonitor(data); + WaitPkg.WaitForUserLevelMonitor(control: 1, counter: 1); + } + } + + [Fact] + public static unsafe void TestTimedPause() + { + if (WaitPkg.IsSupported) + { + RunBasicScenario(); + } + else + { + Assert.Throws(RunBasicScenario); + } + + static void RunBasicScenario() + { + // We're really just testing this doesn't throw. So + // indicate a fast wakeup and the lowest counter possible + + byte* data = stackalloc byte[1]; + WaitPkg.TimedPause(control: 1, counter: 1); + } + } + } +} diff --git a/src/tests/JIT/HardwareIntrinsics/X86/X86Serialize_X64/Serialize.X64_r.csproj b/src/tests/JIT/HardwareIntrinsics/X86/WaitPkg/WaitPkg_r.csproj similarity index 82% rename from src/tests/JIT/HardwareIntrinsics/X86/X86Serialize_X64/Serialize.X64_r.csproj rename to src/tests/JIT/HardwareIntrinsics/X86/WaitPkg/WaitPkg_r.csproj index 7a215f5a0f39be..818f86f1c3cefb 100644 --- a/src/tests/JIT/HardwareIntrinsics/X86/X86Serialize_X64/Serialize.X64_r.csproj +++ b/src/tests/JIT/HardwareIntrinsics/X86/WaitPkg/WaitPkg_r.csproj @@ -1,5 +1,6 @@ + WaitPkg_r true @@ -10,6 +11,6 @@ - + diff --git a/src/tests/JIT/HardwareIntrinsics/X86/X86Serialize_X64/Serialize.X64_ro.csproj b/src/tests/JIT/HardwareIntrinsics/X86/WaitPkg/WaitPkg_ro.csproj similarity index 82% rename from src/tests/JIT/HardwareIntrinsics/X86/X86Serialize_X64/Serialize.X64_ro.csproj rename to src/tests/JIT/HardwareIntrinsics/X86/WaitPkg/WaitPkg_ro.csproj index ae4509b2f4a04d..42f1097c35ac20 100644 --- a/src/tests/JIT/HardwareIntrinsics/X86/X86Serialize_X64/Serialize.X64_ro.csproj +++ b/src/tests/JIT/HardwareIntrinsics/X86/WaitPkg/WaitPkg_ro.csproj @@ -1,5 +1,6 @@ + WaitPkg_ro true @@ -10,6 +11,6 @@ True - + diff --git a/src/tests/JIT/HardwareIntrinsics/X86/X86Base/CpuId.cs b/src/tests/JIT/HardwareIntrinsics/X86/X86Base/CpuId.cs index d2023b8a6911a0..7d16008e6d4236 100644 --- a/src/tests/JIT/HardwareIntrinsics/X86/X86Base/CpuId.cs +++ b/src/tests/JIT/HardwareIntrinsics/X86/X86Base/CpuId.cs @@ -259,10 +259,10 @@ public unsafe static void CpuId() isHierarchyDisabled = isBaselineHierarchyDisabled; - // if (IsBitIncorrect(ecx, 5, typeof(WaitPkg), WaitPkg.IsSupported, "WAITPKG", ref isHierarchyDisabled)) - // { - // testResult = Fail; - // } + if (IsBitIncorrect(ecx, 5, typeof(WaitPkg), WaitPkg.IsSupported, "WAITPKG", ref isHierarchyDisabled)) + { + testResult = Fail; + } isHierarchyDisabled = isBaselineHierarchyDisabled; diff --git a/src/tests/JIT/HardwareIntrinsics/X86/X86Serialize_X64/Serialize.X64.cs b/src/tests/JIT/HardwareIntrinsics/X86/X86Serialize_X64/Serialize.X64.cs deleted file mode 100644 index 15a8d3d87740fc..00000000000000 --- a/src/tests/JIT/HardwareIntrinsics/X86/X86Serialize_X64/Serialize.X64.cs +++ /dev/null @@ -1,36 +0,0 @@ -// 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.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; -using Xunit; - -namespace IntelHardwareIntrinsicTest -{ - public class Program - { - const int Pass = 100; - const int Fail = 0; - - [Fact] - public static unsafe int TestEntryPoint() - { - int testResult = X86Serialize.X64.IsSupported ? Pass : Fail; - - try - { - X86Serialize.Serialize(); - } - catch (Exception e) - { - testResult = (X86Serialize.X64.IsSupported || (e is not PlatformNotSupportedException)) ? Fail : Pass; - } - - return testResult; - } - } -} diff --git a/src/tests/nativeaot/SmokeTests/HardwareIntrinsics/Program.cs b/src/tests/nativeaot/SmokeTests/HardwareIntrinsics/Program.cs index 2c82ac22950674..7d3268d3d8cd7a 100644 --- a/src/tests/nativeaot/SmokeTests/HardwareIntrinsics/Program.cs +++ b/src/tests/nativeaot/SmokeTests/HardwareIntrinsics/Program.cs @@ -467,8 +467,8 @@ static int Main() Check("Pclmulqdq.V256", ExpectedPclmulqdqV256, &PclmulqdqV256IsSupported, Pclmulqdq.V256.IsSupported, () => Pclmulqdq.V256.CarrylessMultiply(Vector256.Zero, Vector256.Zero, 0).Equals(Vector256.Zero)); Check("Pclmulqdq.V512", ExpectedPclmulqdqV512, &PclmulqdqV512IsSupported, Pclmulqdq.V512.IsSupported, () => Pclmulqdq.V512.CarrylessMultiply(Vector512.Zero, Vector512.Zero, 0).Equals(Vector512.Zero)); - // Check("WaitPkg", ExpectedWaitPkg, &WaitPkgIsSupported, WaitPkg.IsSupported, null); - // Check("WaitPkg.X64", ExpectedWaitPkg, &WaitPkgX64IsSupported, WaitPkg.X64.IsSupported, null); + Check("WaitPkg", ExpectedWaitPkg, &WaitPkgIsSupported, WaitPkg.IsSupported, null); + Check("WaitPkg.X64", ExpectedWaitPkg, &WaitPkgX64IsSupported, WaitPkg.X64.IsSupported, null); Check("X86Serialize", ExpectedX86Serialize, &X86SerializeIsSupported, X86Serialize.IsSupported, () => { X86Serialize.Serialize(); return true; } ); Check("X86Serialize.X64", ExpectedX86Serialize, &X86SerializeX64IsSupported, X86Serialize.X64.IsSupported, null); @@ -603,8 +603,8 @@ static int Main() static bool PclmulqdqV256IsSupported() => Pclmulqdq.V256.IsSupported; static bool PclmulqdqV512IsSupported() => Pclmulqdq.V512.IsSupported; - // static bool WaitPkgIsSupported() => WaitPkg.IsSupported; - // static bool WaitPkgX64IsSupported() => WaitPkg.X64.IsSupported; + static bool WaitPkgIsSupported() => WaitPkg.IsSupported; + static bool WaitPkgX64IsSupported() => WaitPkg.X64.IsSupported; static bool X86SerializeIsSupported() => X86Serialize.IsSupported; static bool X86SerializeX64IsSupported() => X86Serialize.X64.IsSupported;