From b9ef92f05c4913dc39fc222c182a1d435973d659 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Sun, 26 Sep 2021 01:54:28 +0300 Subject: [PATCH] JIT: Convert Interlocked intrinsics to NamedIntrinsics (#55170) --- .../System/Threading/Interlocked.CoreCLR.cs | 3 + src/coreclr/inc/corinfo.h | 10 - src/coreclr/inc/jiteeversionguid.h | 10 +- src/coreclr/jit/importer.cpp | 204 +++++++++--------- src/coreclr/jit/namedintrinsiclist.h | 5 + .../JitInterface/CorInfoImpl.Intrinsics.cs | 30 --- .../tools/Common/JitInterface/CorInfoTypes.cs | 10 - src/coreclr/vm/ecalllist.h | 17 +- 8 files changed, 128 insertions(+), 161 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Interlocked.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Interlocked.CoreCLR.cs index 15a9cc87b7e6..520bdb718a83 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Interlocked.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Interlocked.CoreCLR.cs @@ -57,6 +57,7 @@ public static partial class Interlocked /// The value to which the parameter is set. /// The original value of . /// The address of location1 is a null pointer. + [Intrinsic] [MethodImpl(MethodImplOptions.InternalCall)] public static extern long Exchange(ref long location1, long value); @@ -117,6 +118,7 @@ public static partial class Interlocked /// The value that is compared to the value at . /// The original value in . /// The address of is a null pointer. + [Intrinsic] [MethodImpl(MethodImplOptions.InternalCall)] public static extern long CompareExchange(ref long location1, long value, long comparand); @@ -192,6 +194,7 @@ public static partial class Interlocked [MethodImpl(MethodImplOptions.InternalCall)] private static extern int ExchangeAdd(ref int location1, int value); + [Intrinsic] [MethodImpl(MethodImplOptions.InternalCall)] private static extern long ExchangeAdd(ref long location1, long value); #endregion diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index a0093501b561..e552d195fbf7 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -892,16 +892,6 @@ enum CorInfoIntrinsics CORINFO_INTRINSIC_StubHelpers_GetStubContextAddr, CORINFO_INTRINSIC_StubHelpers_NextCallReturnAddress, - CORINFO_INTRINSIC_InterlockedAdd32, - CORINFO_INTRINSIC_InterlockedAdd64, - CORINFO_INTRINSIC_InterlockedXAdd32, - CORINFO_INTRINSIC_InterlockedXAdd64, - CORINFO_INTRINSIC_InterlockedXchg32, - CORINFO_INTRINSIC_InterlockedXchg64, - CORINFO_INTRINSIC_InterlockedCmpXchg32, - CORINFO_INTRINSIC_InterlockedCmpXchg64, - CORINFO_INTRINSIC_MemoryBarrier, - CORINFO_INTRINSIC_MemoryBarrierLoad, CORINFO_INTRINSIC_ByReference_Ctor, CORINFO_INTRINSIC_ByReference_Value, CORINFO_INTRINSIC_GetRawHandle, diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 092ac22313af..6aca52f5884e 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* 4ef06a0e-e58d-4796-abb7-0cda6460610c */ - 0x4ef06a0e, - 0xe58d, - 0x4796, - {0xab, 0xb7, 0xc, 0xda, 0x64, 0x60, 0x61, 0xc} +constexpr GUID JITEEVersionIdentifier = { /* 802cceb2-2ebd-4ff9-ac31-4c3546a02aa5 */ + 0x802cceb2, + 0x2ebd, + 0x4ff9, + {0xac, 0x31, 0x4c, 0x35, 0x46, 0xa0, 0x2a, 0xa5} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index f31f5df43098..5bfa06c31bad 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -3734,10 +3734,6 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, *pIntrinsicID = intrinsicID; -#ifndef TARGET_ARM - genTreeOps interlockedOperator; -#endif - if (intrinsicID == CORINFO_INTRINSIC_StubHelpers_GetStubContext) { // must be done regardless of DbgCode and MinOpts @@ -3785,96 +3781,6 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, { GenTree* op1; -#if defined(TARGET_XARCH) || defined(TARGET_ARM64) - // TODO-ARM-CQ: reenable treating Interlocked operation as intrinsic - - // Note that CORINFO_INTRINSIC_InterlockedAdd32/64 are not actually used. - // Anyway, we can import them as XADD and leave it to lowering/codegen to perform - // whatever optimizations may arise from the fact that result value is not used. - case CORINFO_INTRINSIC_InterlockedAdd32: - case CORINFO_INTRINSIC_InterlockedXAdd32: - interlockedOperator = GT_XADD; - goto InterlockedBinOpCommon; - case CORINFO_INTRINSIC_InterlockedXchg32: - interlockedOperator = GT_XCHG; - goto InterlockedBinOpCommon; - -#ifdef TARGET_64BIT - case CORINFO_INTRINSIC_InterlockedAdd64: - case CORINFO_INTRINSIC_InterlockedXAdd64: - interlockedOperator = GT_XADD; - goto InterlockedBinOpCommon; - case CORINFO_INTRINSIC_InterlockedXchg64: - interlockedOperator = GT_XCHG; - goto InterlockedBinOpCommon; -#endif // TARGET_AMD64 - - InterlockedBinOpCommon: - assert(callType != TYP_STRUCT); - assert(sig->numArgs == 2); - - GenTree* op2; - op2 = impPopStack().val; - op1 = impPopStack().val; - - // This creates: - // val - // XAdd - // addr - // field (for example) - // - // In the case where the first argument is the address of a local, we might - // want to make this *not* make the var address-taken -- but atomic instructions - // on a local are probably pretty useless anyway, so we probably don't care. - - op1 = gtNewOperNode(interlockedOperator, genActualType(callType), op1, op2); - op1->gtFlags |= GTF_GLOB_REF | GTF_ASG; - retNode = op1; - break; -#endif // defined(TARGET_XARCH) || defined(TARGET_ARM64) - - case CORINFO_INTRINSIC_MemoryBarrier: - case CORINFO_INTRINSIC_MemoryBarrierLoad: - - assert(sig->numArgs == 0); - - op1 = new (this, GT_MEMORYBARRIER) GenTree(GT_MEMORYBARRIER, TYP_VOID); - op1->gtFlags |= GTF_GLOB_REF | GTF_ASG; - - // On XARCH `CORINFO_INTRINSIC_MemoryBarrierLoad` fences need not be emitted. - // However, we still need to capture the effect on reordering. - if (intrinsicID == CORINFO_INTRINSIC_MemoryBarrierLoad) - { - op1->gtFlags |= GTF_MEMORYBARRIER_LOAD; - } - - retNode = op1; - break; - -#if defined(TARGET_XARCH) || defined(TARGET_ARM64) - // TODO-ARM-CQ: reenable treating InterlockedCmpXchg32 operation as intrinsic - case CORINFO_INTRINSIC_InterlockedCmpXchg32: -#ifdef TARGET_64BIT - case CORINFO_INTRINSIC_InterlockedCmpXchg64: -#endif - { - assert(callType != TYP_STRUCT); - assert(sig->numArgs == 3); - GenTree* op2; - GenTree* op3; - - op3 = impPopStack().val; // comparand - op2 = impPopStack().val; // value - op1 = impPopStack().val; // location - - GenTree* node = new (this, GT_CMPXCHG) GenTreeCmpXchg(genActualType(callType), op1, op2, op3); - - node->AsCmpXchg()->gtOpLocation->gtFlags |= GTF_DONT_CSE; - retNode = node; - break; - } -#endif // defined(TARGET_XARCH) || defined(TARGET_ARM64) - case CORINFO_INTRINSIC_InitializeArray: retNode = impInitializeArrayIntrinsic(sig); break; @@ -4372,6 +4278,90 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis, } #endif // TARGET_ARM64 +#if defined(TARGET_XARCH) || defined(TARGET_ARM64) + // TODO-ARM-CQ: reenable treating InterlockedCmpXchg32 operation as intrinsic + case NI_System_Threading_Interlocked_CompareExchange: + { + var_types retType = JITtype2varType(sig->retType); + if ((retType == TYP_LONG) && (TARGET_POINTER_SIZE == 4)) + { + break; + } + if ((retType != TYP_INT) && (retType != TYP_LONG)) + { + break; + } + + assert(callType != TYP_STRUCT); + assert(sig->numArgs == 3); + + GenTree* op3 = impPopStack().val; // comparand + GenTree* op2 = impPopStack().val; // value + GenTree* op1 = impPopStack().val; // location + + GenTree* node = new (this, GT_CMPXCHG) GenTreeCmpXchg(genActualType(callType), op1, op2, op3); + + node->AsCmpXchg()->gtOpLocation->gtFlags |= GTF_DONT_CSE; + retNode = node; + break; + } + + case NI_System_Threading_Interlocked_Exchange: + case NI_System_Threading_Interlocked_ExchangeAdd: + { + assert(callType != TYP_STRUCT); + assert(sig->numArgs == 2); + + var_types retType = JITtype2varType(sig->retType); + if ((retType == TYP_LONG) && (TARGET_POINTER_SIZE == 4)) + { + break; + } + if ((retType != TYP_INT) && (retType != TYP_LONG)) + { + break; + } + + GenTree* op2 = impPopStack().val; + GenTree* op1 = impPopStack().val; + + // This creates: + // val + // XAdd + // addr + // field (for example) + // + // In the case where the first argument is the address of a local, we might + // want to make this *not* make the var address-taken -- but atomic instructions + // on a local are probably pretty useless anyway, so we probably don't care. + + op1 = gtNewOperNode(ni == NI_System_Threading_Interlocked_ExchangeAdd ? GT_XADD : GT_XCHG, + genActualType(callType), op1, op2); + op1->gtFlags |= GTF_GLOB_REF | GTF_ASG; + retNode = op1; + break; + } +#endif // defined(TARGET_XARCH) || defined(TARGET_ARM64) + + case NI_System_Threading_Interlocked_MemoryBarrier: + case NI_System_Threading_Interlocked_ReadMemoryBarrier: + { + assert(sig->numArgs == 0); + + GenTree* op1 = new (this, GT_MEMORYBARRIER) GenTree(GT_MEMORYBARRIER, TYP_VOID); + op1->gtFlags |= GTF_GLOB_REF | GTF_ASG; + + // On XARCH `NI_System_Threading_Interlocked_ReadMemoryBarrier` fences need not be emitted. + // However, we still need to capture the effect on reordering. + if (ni == NI_System_Threading_Interlocked_ReadMemoryBarrier) + { + op1->gtFlags |= GTF_MEMORYBARRIER_LOAD; + } + + retNode = op1; + break; + } + #ifdef FEATURE_HW_INTRINSICS case NI_System_Math_FusedMultiplyAdd: { @@ -4962,10 +4952,10 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) result = NI_System_Threading_Thread_get_ManagedThreadId; } } -#ifndef TARGET_ARM64 - // TODO-CQ: Implement for XArch (https://github.com/dotnet/runtime/issues/32239). else if (strcmp(className, "Interlocked") == 0) { +#ifndef TARGET_ARM64 + // TODO-CQ: Implement for XArch (https://github.com/dotnet/runtime/issues/32239). if (strcmp(methodName, "And") == 0) { result = NI_System_Threading_Interlocked_And; @@ -4974,8 +4964,28 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method) { result = NI_System_Threading_Interlocked_Or; } - } #endif + if (strcmp(methodName, "CompareExchange") == 0) + { + result = NI_System_Threading_Interlocked_CompareExchange; + } + else if (strcmp(methodName, "Exchange") == 0) + { + result = NI_System_Threading_Interlocked_Exchange; + } + else if (strcmp(methodName, "ExchangeAdd") == 0) + { + result = NI_System_Threading_Interlocked_ExchangeAdd; + } + else if (strcmp(methodName, "MemoryBarrier") == 0) + { + result = NI_System_Threading_Interlocked_MemoryBarrier; + } + else if (strcmp(methodName, "ReadMemoryBarrier") == 0) + { + result = NI_System_Threading_Interlocked_ReadMemoryBarrier; + } + } } #if defined(TARGET_XARCH) || defined(TARGET_ARM64) else if (strcmp(namespaceName, "System.Buffers.Binary") == 0) diff --git a/src/coreclr/jit/namedintrinsiclist.h b/src/coreclr/jit/namedintrinsiclist.h index 8d162ab3421f..e972b4ad68cd 100644 --- a/src/coreclr/jit/namedintrinsiclist.h +++ b/src/coreclr/jit/namedintrinsiclist.h @@ -73,6 +73,11 @@ enum NamedIntrinsic : unsigned short NI_System_Threading_Interlocked_And, NI_System_Threading_Interlocked_Or, + NI_System_Threading_Interlocked_CompareExchange, + NI_System_Threading_Interlocked_Exchange, + NI_System_Threading_Interlocked_ExchangeAdd, + NI_System_Threading_Interlocked_MemoryBarrier, + NI_System_Threading_Interlocked_ReadMemoryBarrier, #ifdef FEATURE_HW_INTRINSICS NI_HW_INTRINSIC_START, diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.Intrinsics.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.Intrinsics.cs index e55413b11186..ac2eb0668591 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.Intrinsics.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.Intrinsics.cs @@ -85,16 +85,6 @@ static IntrinsicHashtable InitializeIntrinsicHashtable() table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_StubHelpers_GetStubContext, "GetStubContext", "System.StubHelpers", "StubHelpers"); // interop-specific // table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_StubHelpers_GetStubContextAddr, "GetStubContextAddr", "System.StubHelpers", "StubHelpers"); // interop-specific table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_StubHelpers_NextCallReturnAddress, "NextCallReturnAddress", "System.StubHelpers", "StubHelpers"); - // table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_InterlockedAdd32, "Add", System.Threading", "Interlocked"); // unused - // table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_InterlockedAdd64, "Add", System.Threading", "Interlocked"); // unused - table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_InterlockedXAdd32, "ExchangeAdd", "System.Threading", "Interlocked"); - // table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_InterlockedXAdd64, "ExchangeAdd", "System.Threading", "Interlocked"); // ambiguous match - table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_InterlockedXchg32, "Exchange", "System.Threading", "Interlocked"); - // table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_InterlockedXchg64, "Exchange", "System.Threading", "Interlocked"); // ambiguous match - table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_InterlockedCmpXchg32, "CompareExchange", "System.Threading", "Interlocked"); - // table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_InterlockedCmpXchg64, "CompareExchange", "System.Threading", "Interlocked"); // ambiguous match - table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_MemoryBarrier, "MemoryBarrier", "System.Threading", "Interlocked"); - table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_MemoryBarrierLoad, "LoadBarrier", "System.Threading", "Interlocked"); table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_ByReference_Ctor, ".ctor", "System", "ByReference`1"); table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_ByReference_Value, "get_Value", "System", "ByReference`1"); table.Add(CorInfoIntrinsics.CORINFO_INTRINSIC_GetRawHandle, "EETypePtrOf", "System", "EETypePtr"); @@ -145,26 +135,6 @@ private CorInfoIntrinsics getIntrinsicID(MethodDesc method, byte* pMustExpand) return CorInfoIntrinsics.CORINFO_INTRINSIC_Illegal; break; - case CorInfoIntrinsics.CORINFO_INTRINSIC_InterlockedXAdd32: - case CorInfoIntrinsics.CORINFO_INTRINSIC_InterlockedXchg32: - case CorInfoIntrinsics.CORINFO_INTRINSIC_InterlockedCmpXchg32: - { - // RyuJIT handles int32 and int64 overloads only - var returnTypeCategory = method.Signature.ReturnType.Category; - if (returnTypeCategory != TypeFlags.Int32 && returnTypeCategory != TypeFlags.Int64 && returnTypeCategory != TypeFlags.IntPtr) - return CorInfoIntrinsics.CORINFO_INTRINSIC_Illegal; - - // int64 overloads have different ids - if (returnTypeCategory == TypeFlags.Int64) - { - Debug.Assert((int)CorInfoIntrinsics.CORINFO_INTRINSIC_InterlockedXAdd32 + 1 == (int)CorInfoIntrinsics.CORINFO_INTRINSIC_InterlockedXAdd64); - Debug.Assert((int)CorInfoIntrinsics.CORINFO_INTRINSIC_InterlockedXchg32 + 1 == (int)CorInfoIntrinsics.CORINFO_INTRINSIC_InterlockedXchg64); - Debug.Assert((int)CorInfoIntrinsics.CORINFO_INTRINSIC_InterlockedCmpXchg32 + 1 == (int)CorInfoIntrinsics.CORINFO_INTRINSIC_InterlockedCmpXchg64); - id = (CorInfoIntrinsics)((int)id + 1); - } - } - break; - case CorInfoIntrinsics.CORINFO_INTRINSIC_RTH_GetValueInternal: #if !READYTORUN case CorInfoIntrinsics.CORINFO_INTRINSIC_InitializeArray: diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 360ffc2ab635..087ab9a5c273 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -448,16 +448,6 @@ public enum CorInfoIntrinsics CORINFO_INTRINSIC_StubHelpers_GetStubContext, CORINFO_INTRINSIC_StubHelpers_GetStubContextAddr, CORINFO_INTRINSIC_StubHelpers_NextCallReturnAddress, - CORINFO_INTRINSIC_InterlockedAdd32, - CORINFO_INTRINSIC_InterlockedAdd64, - CORINFO_INTRINSIC_InterlockedXAdd32, - CORINFO_INTRINSIC_InterlockedXAdd64, - CORINFO_INTRINSIC_InterlockedXchg32, - CORINFO_INTRINSIC_InterlockedXchg64, - CORINFO_INTRINSIC_InterlockedCmpXchg32, - CORINFO_INTRINSIC_InterlockedCmpXchg64, - CORINFO_INTRINSIC_MemoryBarrier, - CORINFO_INTRINSIC_MemoryBarrierLoad, CORINFO_INTRINSIC_ByReference_Ctor, CORINFO_INTRINSIC_ByReference_Value, CORINFO_INTRINSIC_GetRawHandle, diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index b8bf08d5289f..a93343b112a4 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -803,21 +803,20 @@ FCFuncStart(gMissingMemberExceptionFuncs) FCFuncEnd() FCFuncStart(gInterlockedFuncs) - FCIntrinsicSig("Exchange", &gsig_SM_RefInt_Int_RetInt, COMInterlocked::Exchange, CORINFO_INTRINSIC_InterlockedXchg32) - FCIntrinsicSig("Exchange", &gsig_SM_RefLong_Long_RetLong, COMInterlocked::Exchange64, CORINFO_INTRINSIC_InterlockedXchg64) + FCFuncElementSig("Exchange", &gsig_SM_RefInt_Int_RetInt, COMInterlocked::Exchange) + FCFuncElementSig("Exchange", &gsig_SM_RefLong_Long_RetLong, COMInterlocked::Exchange64) FCFuncElementSig("Exchange", &gsig_SM_RefDbl_Dbl_RetDbl, COMInterlocked::ExchangeDouble) FCFuncElementSig("Exchange", &gsig_SM_RefFlt_Flt_RetFlt, COMInterlocked::ExchangeFloat) FCFuncElementSig("Exchange", &gsig_SM_RefObj_Obj_RetObj, COMInterlocked::ExchangeObject) - FCIntrinsicSig("CompareExchange", &gsig_SM_RefInt_Int_Int_RetInt, COMInterlocked::CompareExchange, CORINFO_INTRINSIC_InterlockedCmpXchg32) - FCIntrinsicSig("CompareExchange", &gsig_SM_RefLong_Long_Long_RetLong, COMInterlocked::CompareExchange64, CORINFO_INTRINSIC_InterlockedCmpXchg64) + FCFuncElementSig("CompareExchange", &gsig_SM_RefInt_Int_Int_RetInt, COMInterlocked::CompareExchange) + FCFuncElementSig("CompareExchange", &gsig_SM_RefLong_Long_Long_RetLong, COMInterlocked::CompareExchange64) FCFuncElementSig("CompareExchange", &gsig_SM_RefDbl_Dbl_Dbl_RetDbl, COMInterlocked::CompareExchangeDouble) FCFuncElementSig("CompareExchange", &gsig_SM_RefFlt_Flt_Flt_RetFlt, COMInterlocked::CompareExchangeFloat) FCFuncElementSig("CompareExchange", &gsig_SM_RefObj_Obj_Obj_RetObj, COMInterlocked::CompareExchangeObject) - FCIntrinsicSig("ExchangeAdd", &gsig_SM_RefInt_Int_RetInt, COMInterlocked::ExchangeAdd32, CORINFO_INTRINSIC_InterlockedXAdd32) - FCIntrinsicSig("ExchangeAdd", &gsig_SM_RefLong_Long_RetLong, COMInterlocked::ExchangeAdd64, CORINFO_INTRINSIC_InterlockedXAdd64) - - FCIntrinsic("MemoryBarrier", COMInterlocked::FCMemoryBarrier, CORINFO_INTRINSIC_MemoryBarrier) - FCIntrinsic("ReadMemoryBarrier", COMInterlocked::FCMemoryBarrierLoad, CORINFO_INTRINSIC_MemoryBarrierLoad) + FCFuncElementSig("ExchangeAdd", &gsig_SM_RefInt_Int_RetInt, COMInterlocked::ExchangeAdd32) + FCFuncElementSig("ExchangeAdd", &gsig_SM_RefLong_Long_RetLong, COMInterlocked::ExchangeAdd64) + FCFuncElement("MemoryBarrier", COMInterlocked::FCMemoryBarrier) + FCFuncElement("ReadMemoryBarrier", COMInterlocked::FCMemoryBarrierLoad) QCFuncElement("_MemoryBarrierProcessWide", COMInterlocked::MemoryBarrierProcessWide) FCFuncEnd()