From 78ede327fe8c4322cf14e75e30c2f06b2ccca32b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Tue, 17 Dec 2024 19:31:57 +0100 Subject: [PATCH 01/25] Fix `DiagnosticMethodInfo.Create(Delegate)` for valuetype methods (#110782) Fixes #108688. We actually have test coverage for this here: https://github.com/dotnet/runtime/blob/ce9dd2ad46a4842f5cf0f03c4a30b4d29bd0b4cc/src/libraries/System.Diagnostics.StackTrace/tests/DiagnosticMethodInfoTests.cs#L137-L147 But hitting the bug requires not considering the method to be target of reflection. We root libraries tests due to xUnit. Reflection can deal with both boxed and unboxed entrypoints, stack trace metadata can't. --- .../src/System/Delegate.cs | 6 +- .../StackTraceMetadata/StackTraceMetadata.cs | 170 ++++++++++++++++++ 2 files changed, 175 insertions(+), 1 deletion(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs index 2ad2166546121..6c404dbe686a2 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs @@ -313,7 +313,11 @@ internal DiagnosticMethodInfo GetDiagnosticMethodInfo() } else { - functionPointer = ldftnResult; + nint unboxedPointer = RuntimeAugments.GetCodeTarget(ldftnResult); + if (unboxedPointer == ldftnResult) + unboxedPointer = RuntimeAugments.GetTargetOfUnboxingAndInstantiatingStub(ldftnResult); + + functionPointer = unboxedPointer != 0 ? unboxedPointer : ldftnResult; } return RuntimeAugments.StackTraceCallbacksIfAvailable?.TryGetDiagnosticMethodInfoFromStartAddress(functionPointer); } diff --git a/src/tests/nativeaot/SmokeTests/StackTraceMetadata/StackTraceMetadata.cs b/src/tests/nativeaot/SmokeTests/StackTraceMetadata/StackTraceMetadata.cs index 7c6017c898879..392502dca0c50 100644 --- a/src/tests/nativeaot/SmokeTests/StackTraceMetadata/StackTraceMetadata.cs +++ b/src/tests/nativeaot/SmokeTests/StackTraceMetadata/StackTraceMetadata.cs @@ -3,6 +3,8 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; using System.Runtime.CompilerServices; class Program @@ -11,6 +13,7 @@ static int Main() { BodyFoldingTest.Run(); DiagnosticMethodInfoTests.Run(); + Test108688Regression.Run(); string stackTrace = Environment.StackTrace; @@ -83,4 +86,171 @@ public class Nested } } } + + class Test108688Regression + { + public static void Run() + { + { + DelStruct s; + Action del = s.InstanceMethod; + var dmi = DiagnosticMethodInfo.Create(del); +#if STRIPPED + if (dmi != null) + throw new Exception(); +#else + if (dmi.Name != nameof(DelStruct.InstanceMethod)) + throw new Exception(); + // Need to make sure it was stack trace metadata and not reflection metadata that provided this + if (GetMethodSecretly(del.Target.GetType(), dmi.Name) != null) + throw new Exception(); +#endif + } + + { + DelStruct s; + Action del = s.InstanceGenericMethod; + var dmi = DiagnosticMethodInfo.Create(del); +#if STRIPPED + if (dmi != null) + throw new Exception(); +#else + if (dmi.Name != nameof(DelStruct.InstanceGenericMethod)) + throw new Exception(); + // Need to make sure it was stack trace metadata and not reflection metadata that provided this + if (GetMethodSecretly(del.Target.GetType(), dmi.Name) != null) + throw new Exception(); +#endif + } + + { + DelStruct s; + Action del = s.InstanceGenericMethod; + var dmi = DiagnosticMethodInfo.Create(del); +#if STRIPPED + if (dmi != null) + throw new Exception(); +#else + if (dmi.Name != nameof(DelStruct.InstanceGenericMethod)) + throw new Exception(); + // Need to make sure it was stack trace metadata and not reflection metadata that provided this + if (GetMethodSecretly(del.Target.GetType(), dmi.Name) != null) + throw new Exception(); +#endif + } + + { + DelStruct s; + Action del = s.InstanceMethod; + var dmi = DiagnosticMethodInfo.Create(del); +#if STRIPPED + if (dmi != null) + throw new Exception(); +#else + if (dmi.Name != nameof(DelStruct.InstanceMethod)) + throw new Exception(); + // Need to make sure it was stack trace metadata and not reflection metadata that provided this + if (GetMethodSecretly(del.Target.GetType(), dmi.Name) != null) + throw new Exception(); +#endif + } + + { + DelStruct s; + Action del = s.InstanceGenericMethod; + var dmi = DiagnosticMethodInfo.Create(del); +#if STRIPPED + if (dmi != null) + throw new Exception(); +#else + if (dmi.Name != nameof(DelStruct.InstanceGenericMethod)) + throw new Exception(); + // Need to make sure it was stack trace metadata and not reflection metadata that provided this + if (GetMethodSecretly(del.Target.GetType(), dmi.Name) != null) + throw new Exception(); +#endif + } + + { + DelStruct s; + Action del = s.InstanceGenericMethod; + var dmi = DiagnosticMethodInfo.Create(del); +#if STRIPPED + if (dmi != null) + throw new Exception(); +#else + if (dmi.Name != nameof(DelStruct.InstanceGenericMethod)) + throw new Exception(); + // Need to make sure it was stack trace metadata and not reflection metadata that provided this + if (GetMethodSecretly(del.Target.GetType(), dmi.Name) != null) + throw new Exception(); +#endif + } + + { + DelStruct s; + Action del = s.InstanceMethod; + var dmi = DiagnosticMethodInfo.Create(del); +#if STRIPPED + if (dmi != null) + throw new Exception(); +#else + if (dmi.Name != nameof(DelStruct.InstanceMethod)) + throw new Exception(); + // Need to make sure it was stack trace metadata and not reflection metadata that provided this + if (GetMethodSecretly(del.Target.GetType(), dmi.Name) != null) + throw new Exception(); +#endif + } + + { + DelStruct s; + Action del = s.InstanceGenericMethod; + var dmi = DiagnosticMethodInfo.Create(del); +#if STRIPPED + if (dmi != null) + throw new Exception(); +#else + if (dmi.Name != nameof(DelStruct.InstanceGenericMethod)) + throw new Exception(); + // Need to make sure it was stack trace metadata and not reflection metadata that provided this + if (GetMethodSecretly(del.Target.GetType(), dmi.Name) != null) + throw new Exception(); +#endif + } + + { + DelStruct s; + Action del = s.InstanceGenericMethod; + var dmi = DiagnosticMethodInfo.Create(del); +#if STRIPPED + if (dmi != null) + throw new Exception(); +#else + if (dmi.Name != nameof(DelStruct.InstanceGenericMethod)) + throw new Exception(); + // Need to make sure it was stack trace metadata and not reflection metadata that provided this + if (GetMethodSecretly(del.Target.GetType(), dmi.Name) != null) + throw new Exception(); +#endif + } + } + + [UnconditionalSuppressMessage("", "IL2070")] + private static MethodInfo GetMethodSecretly(Type t, string name) + => t.GetMethod(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); + + struct DelStruct + { + public void InstanceMethod() { } + public void InstanceGenericMethod() { } + } + + struct DelStruct + { + public void InstanceMethod() { } + public void InstanceGenericMethod() { } + } + } + } From e65457031da52f57d03da721f381a82a5be64c03 Mon Sep 17 00:00:00 2001 From: "Matthew P. Del Buono" Date: Tue, 17 Dec 2024 12:01:35 -0800 Subject: [PATCH 02/25] Update TagName docs to include behavior if tag is not found (#109934) * Clarify documentation for TagList.IndexOf Adds documentation to TagList.IndexOf that states the behavior when the key was not found. * Clarify documentation for TagList.IndexOf Adds documentation to TagList.IndexOf that states the behavior when the key was not found. --- .../src/System/Diagnostics/Metrics/TagList.netcore.cs | 1 + .../src/System/Diagnostics/Metrics/TagList.netfx.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/TagList.netcore.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/TagList.netcore.cs index cd070ebdd31a5..325a0ee380c75 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/TagList.netcore.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/TagList.netcore.cs @@ -266,6 +266,7 @@ public bool Remove(KeyValuePair item) /// Searches for the specified tag and returns the zero-based index of the first occurrence within the entire . /// /// The tag to locate in the . + /// The zero-based index of the first occurrence within the , or -1 if there is no such tag. public readonly int IndexOf(KeyValuePair item) { ReadOnlySpan> tags = diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/TagList.netfx.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/TagList.netfx.cs index f003241ceb40a..4714348bd82a6 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/TagList.netfx.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/TagList.netfx.cs @@ -416,6 +416,7 @@ public bool Remove(KeyValuePair item) /// Searches for the specified tag and returns the zero-based index of the first occurrence within the entire . /// /// The tag to locate in the . + /// The zero-based index of the first occurrence within the , or -1 if there is no such tag. public readonly int IndexOf(KeyValuePair item) { if (_overflowTags is not null) From 8edcc3123615dfe02fbd830505b2acdd5f428bfa Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 17 Dec 2024 20:42:31 -0800 Subject: [PATCH 03/25] Remove `FCThrow`s from Reflection APIs. (#110766) Co-authored-by: Jan Kotas --- .../src/System/Reflection/RuntimeAssembly.cs | 11 +- .../src/System/RuntimeHandles.cs | 11 +- src/coreclr/vm/ecalllist.h | 2 +- src/coreclr/vm/fcall.cpp | 47 ----- src/coreclr/vm/fcall.h | 13 +- src/coreclr/vm/runtimehandles.cpp | 172 +++++------------- src/coreclr/vm/runtimehandles.h | 4 +- 7 files changed, 65 insertions(+), 195 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs index 95bcf0652648a..1bf9c90c51435 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs @@ -727,7 +727,16 @@ static RuntimeModule GetManifestModuleWorker(RuntimeAssembly assembly) private static partial void GetManifestModuleSlow(ObjectHandleOnStack assembly, ObjectHandleOnStack module); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern int GetToken(RuntimeAssembly assembly); + private static extern int GetTokenInternal(RuntimeAssembly assembly); + + internal static int GetToken(RuntimeAssembly assembly) + { + int tokenMaybe = GetTokenInternal(assembly); + // If the result is negative, it is an error code. + if (tokenMaybe < 0) + Marshal.ThrowExceptionForHR(tokenMaybe, new IntPtr(-1)); + return tokenMaybe; + } [RequiresUnreferencedCode("Types might be removed")] public sealed override Type[] GetForwardedTypes() diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index 3246c90a35f47..4bc9b32f20ca3 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -1258,7 +1258,7 @@ internal static bool IsGenericMethodDefinition(IRuntimeMethodInfo method) } [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool IsTypicalMethodDefinition(IRuntimeMethodInfo method); + private static extern bool IsTypicalMethodDefinition(IRuntimeMethodInfo method); [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeMethodHandle_GetTypicalMethodDefinition")] private static partial void GetTypicalMethodDefinition(RuntimeMethodHandleInternal method, ObjectHandleOnStack outMethod); @@ -1626,7 +1626,14 @@ internal static void SetValueDirect(RtFieldInfo field, RuntimeType fieldType, Ty } [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern RuntimeFieldHandleInternal GetStaticFieldForGenericType(RuntimeFieldHandleInternal field, RuntimeType declaringType); + private static extern unsafe RuntimeFieldHandleInternal GetStaticFieldForGenericType(RuntimeFieldHandleInternal field, MethodTable* pMT); + + internal static RuntimeFieldHandleInternal GetStaticFieldForGenericType(RuntimeFieldHandleInternal field, RuntimeType declaringType) + { + TypeHandle th = declaringType.GetNativeTypeHandle(); + Debug.Assert(!th.IsTypeDesc); + return GetStaticFieldForGenericType(field, th.AsMethodTable()); + } [MethodImpl(MethodImplOptions.InternalCall)] internal static extern bool AcquiresContextFromThis(RuntimeFieldHandleInternal field); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index a782ae05d2d41..d6fd867a53ff0 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -169,7 +169,7 @@ FCFuncEnd() FCFuncStart(gRuntimeAssemblyFuncs) FCFuncElement("GetIsDynamic", AssemblyNative::GetIsDynamic) FCFuncElement("GetManifestModule", AssemblyHandle::GetManifestModule) - FCFuncElement("GetToken", AssemblyHandle::GetToken) + FCFuncElement("GetTokenInternal", AssemblyHandle::GetTokenInternal) FCFuncEnd() FCFuncStart(gAssemblyLoadContextFuncs) diff --git a/src/coreclr/vm/fcall.cpp b/src/coreclr/vm/fcall.cpp index 80f163c19b375..fc2e16f98a49b 100644 --- a/src/coreclr/vm/fcall.cpp +++ b/src/coreclr/vm/fcall.cpp @@ -65,53 +65,6 @@ NOINLINE LPVOID __FCThrow(LPVOID __me, RuntimeExceptionKind reKind, UINT resID, return NULL; } -NOINLINE LPVOID __FCThrowArgument(LPVOID __me, RuntimeExceptionKind reKind, LPCWSTR argName, LPCWSTR resourceName) -{ - STATIC_CONTRACT_THROWS; - // This isn't strictly true... But the guarantee that we make here is - // that we won't trigger without having setup a frame. - // STATIC_CONTRACT_TRIGGER - STATIC_CONTRACT_GC_NOTRIGGER; - - // side effect the compiler can't remove - if (FC_NO_TAILCALL != 1) - return (LPVOID)(SIZE_T)(FC_NO_TAILCALL + 1); - - FC_CAN_TRIGGER_GC(); - INCONTRACT(FCallCheck __fCallCheck(__FILE__, __LINE__)); - FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC - - HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_NOPOLL(Frame::FRAME_ATTR_CAPTURE_DEPTH_2); - - switch (reKind) { - case kArgumentNullException: - if (resourceName) { - COMPlusThrowArgumentNull(argName, resourceName); - } else { - COMPlusThrowArgumentNull(argName); - } - break; - - case kArgumentOutOfRangeException: - COMPlusThrowArgumentOutOfRange(argName, resourceName); - break; - - case kArgumentException: - COMPlusThrowArgumentException(argName, resourceName); - break; - - default: - // If you see this assert, add a case for your exception kind above. - _ASSERTE(argName == NULL); - COMPlusThrow(reKind, resourceName); - } - - HELPER_METHOD_FRAME_END(); - FC_CAN_TRIGGER_GC_END(); - _ASSERTE(!"Throw returned"); - return NULL; -} - /**************************************************************************************/ /* erect a frame in the FCALL and then poll the GC, objToProtect will be protected during the poll and the updated object returned. */ diff --git a/src/coreclr/vm/fcall.h b/src/coreclr/vm/fcall.h index 8c56fd8c55f79..3a5bcf5e0ee15 100644 --- a/src/coreclr/vm/fcall.h +++ b/src/coreclr/vm/fcall.h @@ -362,11 +362,9 @@ class CompletedFCallTransitionState //============================================================================================== // This is where FCThrow ultimately ends up. Never call this directly. -// Use the FCThrow() macros. __FCThrowArgument is the helper to throw ArgumentExceptions -// with a resource taken from the managed resource manager. +// Use the FCThrow() macro. //============================================================================================== LPVOID __FCThrow(LPVOID me, enum RuntimeExceptionKind reKind, UINT resID, LPCWSTR arg1, LPCWSTR arg2, LPCWSTR arg3); -LPVOID __FCThrowArgument(LPVOID me, enum RuntimeExceptionKind reKind, LPCWSTR argumentName, LPCWSTR resourceName); //============================================================================================== // FDECLn: A set of macros for generating header declarations for FC targets. @@ -1236,15 +1234,6 @@ struct FCSigCheck { return 0; \ } -// Use FCThrowRes to throw an exception with a localized error message from the -// ResourceManager in managed code. -#define FCThrowRes(reKind, resourceName) \ - { \ - while (NULL == \ - __FCThrowArgument(__me, reKind, NULL, resourceName)) {}; \ - return 0; \ - } - // The managed calling convention expects returned small types (e.g. bool) to be // widened to 32-bit on return. The C/C++ calling convention does not guarantee returned // small types to be widened on most platforms. The small types have to be artificially diff --git a/src/coreclr/vm/runtimehandles.cpp b/src/coreclr/vm/runtimehandles.cpp index b016f70ba60dc..c656784427fb9 100644 --- a/src/coreclr/vm/runtimehandles.cpp +++ b/src/coreclr/vm/runtimehandles.cpp @@ -185,8 +185,10 @@ extern "C" BOOL QCALLTYPE RuntimeTypeHandle_IsEquivalentTo(QCall::TypeHandle rtT } #endif // FEATURE_TYPEEQUIVALENCE -FCIMPL1(MethodDesc *, RuntimeTypeHandle::GetFirstIntroducedMethod, ReflectClassBaseObject *pTypeUNSAFE) { - CONTRACTL { +FCIMPL1(MethodDesc *, RuntimeTypeHandle::GetFirstIntroducedMethod, ReflectClassBaseObject *pTypeUNSAFE) +{ + CONTRACTL + { FCALL_CHECK; PRECONDITION(CheckPointer(pTypeUNSAFE)); } @@ -194,18 +196,11 @@ FCIMPL1(MethodDesc *, RuntimeTypeHandle::GetFirstIntroducedMethod, ReflectClassB REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); TypeHandle typeHandle = refType->GetType(); - - if (typeHandle.IsGenericVariable()) - FCThrowRes(kArgumentException, W("Arg_InvalidHandle")); - - if (typeHandle.IsTypeDesc()) { + _ASSERTE(!typeHandle.IsGenericVariable()); + if (typeHandle.IsTypeDesc()) return NULL; - } MethodTable* pMT = typeHandle.AsMethodTable(); - if (pMT == NULL) - return NULL; - MethodDesc* pMethod = MethodTable::IntroducedMethodIterator::GetFirst(pMT); return pMethod; } @@ -622,22 +617,14 @@ extern "C" void QCALLTYPE RuntimeTypeHandle_GetConstraints(QCall::TypeHandle pTy return; } -FCIMPL1(INT32, RuntimeTypeHandle::GetAttributes, ReflectClassBaseObject *pTypeUNSAFE) { - CONTRACTL { - FCALL_CHECK; - } - CONTRACTL_END; +FCIMPL1(INT32, RuntimeTypeHandle::GetAttributes, ReflectClassBaseObject *pTypeUNSAFE) +{ + FCALL_CONTRACT; REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); - - if (refType == NULL) - FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); - TypeHandle typeHandle = refType->GetType(); - - if (typeHandle.IsTypeDesc()) { + if (typeHandle.IsTypeDesc()) return tdPublic; - } #ifdef FEATURE_COMINTEROP // __ComObject types are always public. @@ -757,19 +744,14 @@ FCIMPL1(LPCUTF8, RuntimeTypeHandle::GetUtf8Name, MethodTable* pMT) } FCIMPLEND -FCIMPL1(INT32, RuntimeTypeHandle::GetToken, ReflectClassBaseObject *pTypeUNSAFE) { - CONTRACTL { - FCALL_CHECK; - } - CONTRACTL_END; +FCIMPL1(INT32, RuntimeTypeHandle::GetToken, ReflectClassBaseObject *pTypeUNSAFE) +{ + FCALL_CONTRACT; REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); - - if (refType == NULL) - FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); + _ASSERTE(refType != NULL); TypeHandle typeHandle = refType->GetType(); - if (typeHandle.IsTypeDesc() || typeHandle.IsArray()) { if (typeHandle.IsGenericVariable()) @@ -1161,10 +1143,6 @@ FCIMPL2(FC_BOOL_RET, RuntimeTypeHandle::CompareCanonicalHandles, ReflectClassBas REFLECTCLASSBASEREF refLeft = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pLeftUNSAFE); REFLECTCLASSBASEREF refRight = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pRightUNSAFE); - - if ((refLeft == NULL) || (refRight == NULL)) - FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); - FC_RETURN_BOOL(refLeft->GetType().GetCanonicalMethodTable() == refRight->GetType().GetCanonicalMethodTable()); } FCIMPLEND @@ -1174,10 +1152,6 @@ FCIMPL1(FC_BOOL_RET, RuntimeTypeHandle::IsGenericVariable, PTR_ReflectClassBaseO FCALL_CONTRACT; REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); - - if (refType == NULL) - FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); - FC_RETURN_BOOL(refType->GetType().IsGenericVariable()); } FCIMPLEND @@ -1187,10 +1161,6 @@ FCIMPL1(INT32, RuntimeTypeHandle::GetGenericVariableIndex, PTR_ReflectClassBaseO FCALL_CONTRACT; REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); - - if (refType == NULL) - FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); - return (INT32)refType->GetType().AsGenericVariable()->GetIndex(); } FCIMPLEND @@ -1200,10 +1170,6 @@ FCIMPL1(FC_BOOL_RET, RuntimeTypeHandle::ContainsGenericVariables, PTR_ReflectCla FCALL_CONTRACT; REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); - - if (refType == NULL) - FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); - FC_RETURN_BOOL(refType->GetType().ContainsGenericVariables()); } FCIMPLEND @@ -1304,33 +1270,23 @@ FCIMPL1(LPCUTF8, RuntimeMethodHandle::GetUtf8Name, MethodDesc* pMethod) } FCIMPLEND -FCIMPL1(INT32, RuntimeMethodHandle::GetAttributes, MethodDesc *pMethod) { - CONTRACTL { - FCALL_CHECK; - } - CONTRACTL_END; - - if (!pMethod) - FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); - +FCIMPL1(INT32, RuntimeMethodHandle::GetAttributes, MethodDesc *pMethod) +{ + FCALL_CONTRACT; + _ASSERTE(pMethod != NULL); return (INT32)pMethod->GetAttrs(); } FCIMPLEND -FCIMPL1(INT32, RuntimeMethodHandle::GetImplAttributes, ReflectMethodObject *pMethodUNSAFE) { - CONTRACTL { - FCALL_CHECK; - } - CONTRACTL_END; - - if (!pMethodUNSAFE) - FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); +FCIMPL1(INT32, RuntimeMethodHandle::GetImplAttributes, ReflectMethodObject *pMethodUNSAFE) +{ + FCALL_CONTRACT; + _ASSERTE(pMethodUNSAFE != NULL); MethodDesc* pMethod = pMethodUNSAFE->GetMethod(); - INT32 attributes = 0; if (IsNilToken(pMethod->GetMemberDef())) - return attributes; + return 0; return (INT32)pMethod->GetImplAttrs(); } @@ -1338,26 +1294,16 @@ FCIMPLEND FCIMPL1(MethodTable*, RuntimeMethodHandle::GetMethodTable, MethodDesc *pMethod) { - CONTRACTL - { - FCALL_CHECK; - PRECONDITION(pMethod != NULL); - } - CONTRACTL_END; - + FCALL_CONTRACT; + _ASSERTE(pMethod != NULL); return pMethod->GetMethodTable(); } FCIMPLEND -FCIMPL1(INT32, RuntimeMethodHandle::GetSlot, MethodDesc *pMethod) { - CONTRACTL { - FCALL_CHECK; - } - CONTRACTL_END; - - if (!pMethod) - FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); - +FCIMPL1(INT32, RuntimeMethodHandle::GetSlot, MethodDesc *pMethod) +{ + FCALL_CONTRACT; + _ASSERTE(pMethod != NULL); return (INT32)pMethod->GetSlot(); } FCIMPLEND @@ -1759,9 +1705,7 @@ FCIMPLEND FCIMPL1(Object*, RuntimeMethodHandle::GetResolver, MethodDesc * pMethod) { FCALL_CONTRACT; - - if (!pMethod) - FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); + _ASSERTE(pMethod != NULL); OBJECTREF resolver = NULL; if (pMethod->IsLCGMethod()) @@ -1803,12 +1747,7 @@ extern "C" void QCALLTYPE RuntimeMethodHandle_Destroy(MethodDesc * pMethod) FCIMPL1(FC_BOOL_RET, RuntimeMethodHandle::IsTypicalMethodDefinition, ReflectMethodObject *pMethodUNSAFE) { FCALL_CONTRACT; - - if (!pMethodUNSAFE) - FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); - MethodDesc* pMethod = pMethodUNSAFE->GetMethod(); - FC_RETURN_BOOL(pMethod->IsTypicalMethodDefinition()); } FCIMPLEND @@ -2178,13 +2117,8 @@ FCIMPLEND FCIMPL1(INT32, RuntimeFieldHandle::GetAttributes, FieldDesc *pField) { - CONTRACTL - { - FCALL_CHECK; - PRECONDITION(pField != NULL); - } - CONTRACTL_END; - + FCALL_CONTRACT; + _ASSERTE(pField != NULL); return (INT32)pField->GetAttributes(); } FCIMPLEND @@ -2217,26 +2151,11 @@ FCIMPL1(INT32, RuntimeFieldHandle::GetToken, FieldDesc* pField) } FCIMPLEND -FCIMPL2(FieldDesc*, RuntimeFieldHandle::GetStaticFieldForGenericType, FieldDesc *pField, ReflectClassBaseObject *pDeclaringTypeUNSAFE) +FCIMPL2(FieldDesc*, RuntimeFieldHandle::GetStaticFieldForGenericType, FieldDesc* pField, MethodTable* pMT) { - CONTRACTL { - FCALL_CHECK; - } - CONTRACTL_END; - - REFLECTCLASSBASEREF refDeclaringType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pDeclaringTypeUNSAFE); - - if ((refDeclaringType == NULL) || (pField == NULL)) - FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); - - TypeHandle declaringType = refDeclaringType->GetType(); - - if (!pField) - FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); - if (declaringType.IsTypeDesc() || declaringType.IsArray()) - FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); - MethodTable *pMT = declaringType.AsMethodTable(); + FCALL_CONTRACT; + _ASSERTE ((pField != NULL) && (pMT != NULL)); _ASSERTE(pField->IsStatic()); if (pMT->HasGenericsStaticsInfo()) pField = pMT->GetFieldDescByIndex(pField->GetApproxEnclosingMethodTable()->GetIndexForFieldDesc(pField)); @@ -2252,8 +2171,7 @@ FCIMPL1(ReflectModuleBaseObject*, AssemblyHandle::GetManifestModule, AssemblyBas FCALL_CONTRACT; ASSEMBLYREF refAssembly = (ASSEMBLYREF)ObjectToOBJECTREF(pAssemblyUNSAFE); - if (refAssembly == NULL) - return NULL; + _ASSERTE(refAssembly != NULL); Module* pModule = refAssembly->GetAssembly()->GetModule(); OBJECTREF refModule = pModule->GetExposedObjectIfExists(); @@ -2261,24 +2179,18 @@ FCIMPL1(ReflectModuleBaseObject*, AssemblyHandle::GetManifestModule, AssemblyBas } FCIMPLEND -FCIMPL1(INT32, AssemblyHandle::GetToken, AssemblyBaseObject* pAssemblyUNSAFE) { +FCIMPL1(INT32, AssemblyHandle::GetTokenInternal, AssemblyBaseObject* pAssemblyUNSAFE) +{ FCALL_CONTRACT; ASSEMBLYREF refAssembly = (ASSEMBLYREF)ObjectToOBJECTREF(pAssemblyUNSAFE); - - if (refAssembly == NULL) - FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); + _ASSERTE(refAssembly != NULL); mdAssembly token = mdAssemblyNil; - IMDInternalImport *mdImport = refAssembly->GetAssembly()->GetMDImport(); - if (mdImport != 0) - { - if (FAILED(mdImport->GetAssemblyFromScope(&token))) - { - FCThrow(kBadImageFormatException); - } - } + IMDInternalImport* mdImport = refAssembly->GetAssembly()->GetMDImport(); + if (FAILED(mdImport->GetAssemblyFromScope(&token))) + return COR_E_BADIMAGEFORMAT; return token; } diff --git a/src/coreclr/vm/runtimehandles.h b/src/coreclr/vm/runtimehandles.h index dd549987bb9fe..914b549495933 100644 --- a/src/coreclr/vm/runtimehandles.h +++ b/src/coreclr/vm/runtimehandles.h @@ -261,7 +261,7 @@ class RuntimeFieldHandle static FCDECL1(INT32, GetAttributes, FieldDesc *pField); static FCDECL1(MethodTable*, GetApproxDeclaringMethodTable, FieldDesc *pField); static FCDECL1(INT32, GetToken, FieldDesc* pField); - static FCDECL2(FieldDesc*, GetStaticFieldForGenericType, FieldDesc *pField, ReflectClassBaseObject *pDeclaringType); + static FCDECL2(FieldDesc*, GetStaticFieldForGenericType, FieldDesc *pField, MethodTable *pMT); static FCDECL1(FC_BOOL_RET, AcquiresContextFromThis, FieldDesc *pField); static FCDECL1(Object*, GetLoaderAllocatorInternal, FieldDesc *pField); }; @@ -292,7 +292,7 @@ class AssemblyHandle { public: static FCDECL1(ReflectModuleBaseObject*, GetManifestModule, AssemblyBaseObject *pAssemblyUNSAFE); - static FCDECL1(INT32, GetToken, AssemblyBaseObject *pAssemblyUNSAFE); + static FCDECL1(INT32, GetTokenInternal, AssemblyBaseObject *pAssemblyUNSAFE); }; extern "C" void QCALLTYPE AssemblyHandle_GetManifestModuleSlow(QCall::ObjectHandleOnStack assembly, QCall::ObjectHandleOnStack module); From f6e46ff86b784db1106e2148d84e43432f96116c Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 18 Dec 2024 11:22:04 +0100 Subject: [PATCH 04/25] [main] Update dependencies from dotnet/roslyn-analyzers (#110119) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20241124.1 Microsoft.CodeAnalysis.Analyzers , Microsoft.CodeAnalysis.NetAnalyzers From Version 3.12.0-beta1.24559.1 -> To Version 3.12.0-beta1.24574.1 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20241202.1 Microsoft.CodeAnalysis.Analyzers , Microsoft.CodeAnalysis.NetAnalyzers From Version 3.12.0-beta1.24559.1 -> To Version 3.12.0-beta1.24602.1 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20241203.2 Microsoft.CodeAnalysis.Analyzers , Microsoft.CodeAnalysis.NetAnalyzers From Version 3.12.0-beta1.24559.1 -> To Version 3.12.0-beta1.24603.2 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20241205.1 Microsoft.CodeAnalysis.Analyzers , Microsoft.CodeAnalysis.NetAnalyzers From Version 3.12.0-beta1.24559.1 -> To Version 3.12.0-beta1.24605.1 * Update dependencies from https://github.com/dotnet/roslyn-analyzers build 20241216.1 Microsoft.CodeAnalysis.Analyzers , Microsoft.CodeAnalysis.NetAnalyzers From Version 3.12.0-beta1.24559.1 -> To Version 3.12.0-beta1.24616.1 --------- Co-authored-by: dotnet-maestro[bot] Co-authored-by: Alexander Köplinger --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 7ab1cf38b41ea..79d1f33d143c0 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -388,13 +388,13 @@ https://github.com/dotnet/roslyn 86d60f7a00b0274a806a40afde8801a89d27e6bc - + https://github.com/dotnet/roslyn-analyzers - 5435ba7b1037f21237adc1b3845f97e9fdbc075d + 5ed336762c6260a83ece35cd1f6749251452bad0 - + https://github.com/dotnet/roslyn-analyzers - 5435ba7b1037f21237adc1b3845f97e9fdbc075d + 5ed336762c6260a83ece35cd1f6749251452bad0 diff --git a/eng/Versions.props b/eng/Versions.props index c5cc32e6f7ab8..e87e3ef977ec4 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -36,8 +36,8 @@ - 3.12.0-beta1.24559.1 - 10.0.0-preview.24559.1 + 3.12.0-beta1.24616.1 + 10.0.0-preview.24616.1 diff --git a/eng/Versions.props b/eng/Versions.props index e87e3ef977ec4..84bd839488a02 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -164,12 +164,12 @@ 10.0.0-alpha.0.24568.1 - 1.0.0-prerelease.24462.2 - 1.0.0-prerelease.24462.2 - 1.0.0-prerelease.24462.2 - 1.0.0-prerelease.24462.2 - 1.0.0-prerelease.24462.2 - 1.0.0-prerelease.24462.2 + 1.0.0-prerelease.24612.6 + 1.0.0-prerelease.24612.6 + 1.0.0-prerelease.24612.6 + 1.0.0-prerelease.24612.6 + 1.0.0-prerelease.24612.6 + 1.0.0-prerelease.24612.6 2.0.0 17.10.0-beta1.24272.1 From 49f414592dddb1bb51f94270c46df276c535868c Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 18 Dec 2024 14:49:31 +0100 Subject: [PATCH 06/25] [main] Update dependencies from dotnet/xharness (#110528) * Update dependencies from https://github.com/dotnet/xharness build 20241204.1 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 10.0.0-prerelease.24575.1 -> To Version 10.0.0-prerelease.24604.1 * Update dependencies from https://github.com/dotnet/xharness build 20241210.1 Microsoft.DotNet.XHarness.CLI , Microsoft.DotNet.XHarness.TestRunners.Common , Microsoft.DotNet.XHarness.TestRunners.Xunit From Version 10.0.0-prerelease.24575.1 -> To Version 10.0.0-prerelease.24610.1 --------- Co-authored-by: dotnet-maestro[bot] --- .config/dotnet-tools.json | 2 +- eng/Version.Details.xml | 12 ++++++------ eng/Versions.props | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 4f24f5cddba82..e5c8a9bf36028 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "10.0.0-prerelease.24575.1", + "version": "10.0.0-prerelease.24610.1", "commands": [ "xharness" ] diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 1647eace8c7db..14a5163f638ae 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -336,17 +336,17 @@ https://github.com/dotnet/runtime 55eee324653e01cf28809d02b25a5b0894b58d22 - + https://github.com/dotnet/xharness - 9fca69998474507f6515c1351efe027dbf194c87 + 3119edb6d70fb252e6128b0c7e45d3fc2f49f249 - + https://github.com/dotnet/xharness - 9fca69998474507f6515c1351efe027dbf194c87 + 3119edb6d70fb252e6128b0c7e45d3fc2f49f249 - + https://github.com/dotnet/xharness - 9fca69998474507f6515c1351efe027dbf194c87 + 3119edb6d70fb252e6128b0c7e45d3fc2f49f249 https://github.com/dotnet/arcade diff --git a/eng/Versions.props b/eng/Versions.props index 84bd839488a02..877bc6758fa96 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -158,9 +158,9 @@ 10.0.0-beta.24571.1 10.0.0-beta.24571.1 - 10.0.0-prerelease.24575.1 - 10.0.0-prerelease.24575.1 - 10.0.0-prerelease.24575.1 + 10.0.0-prerelease.24610.1 + 10.0.0-prerelease.24610.1 + 10.0.0-prerelease.24610.1 10.0.0-alpha.0.24568.1 From bee5ab754942b0d4d2029ae4186043a1dc1211fd Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 18 Dec 2024 14:50:13 +0100 Subject: [PATCH 07/25] [main] Update dependencies from dotnet/roslyn (#110690) * Update dependencies from https://github.com/dotnet/roslyn build 20241213.1 Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.13.0-3.24611.10 -> To Version 4.13.0-3.24613.1 * Update dependencies from https://github.com/dotnet/roslyn build 20241213.14 Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.13.0-3.24611.10 -> To Version 4.13.0-3.24613.14 * Update dependencies from https://github.com/dotnet/roslyn build 20241214.4 Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.13.0-3.24611.10 -> To Version 4.13.0-3.24614.4 * Update dependencies from https://github.com/dotnet/roslyn build 20241217.3 Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.CodeAnalysis , Microsoft.CodeAnalysis.CSharp , Microsoft.Net.Compilers.Toolset From Version 4.13.0-3.24611.10 -> To Version 4.13.0-3.24617.3 --------- Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 16 ++++++++-------- eng/Versions.props | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 14a5163f638ae..5c6170bb63f18 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -376,17 +376,17 @@ https://github.com/dotnet/runtime-assets 4daef9f8f673c1d0bbec273f2bcda1ab9074ef75 - + https://github.com/dotnet/roslyn - 86d60f7a00b0274a806a40afde8801a89d27e6bc + f9ccce030d8fdcc2ea095e095b71316d243c5b35 - + https://github.com/dotnet/roslyn - 86d60f7a00b0274a806a40afde8801a89d27e6bc + f9ccce030d8fdcc2ea095e095b71316d243c5b35 - + https://github.com/dotnet/roslyn - 86d60f7a00b0274a806a40afde8801a89d27e6bc + f9ccce030d8fdcc2ea095e095b71316d243c5b35 https://github.com/dotnet/roslyn-analyzers @@ -397,9 +397,9 @@ 5ed336762c6260a83ece35cd1f6749251452bad0 - + https://github.com/dotnet/roslyn - 86d60f7a00b0274a806a40afde8801a89d27e6bc + f9ccce030d8fdcc2ea095e095b71316d243c5b35 diff --git a/eng/Versions.props b/eng/Versions.props index 877bc6758fa96..11ac136145275 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -44,9 +44,9 @@ Any tools that contribute to the design-time experience should use the MicrosoftCodeAnalysisVersion_LatestVS property above to ensure they do not break the local dev experience. --> - 4.13.0-3.24611.10 - 4.13.0-3.24611.10 - 4.13.0-3.24611.10 + 4.13.0-3.24617.3 + 4.13.0-3.24617.3 + 4.13.0-3.24617.3 - 10.0.0-alpha.0.24568.1 + 10.0.0-alpha.0.24617.1 1.0.0-prerelease.24612.6 1.0.0-prerelease.24612.6 From 3e5b44a569e056fc2752d0f82e5827427b74bffb Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Wed, 18 Dec 2024 21:37:58 +0100 Subject: [PATCH 13/25] Remove FindFirstFile, FindNextFile and FindClose from PAL (#110797) * Remove FindFirstFile, FindNextFile and FindClose from PAL Also remove the related tests * Modify superpmi mcs tool to not to use Find*File on Unix * Fix access after free for string in error path --- .../dlls/mscordac/mscordac_unixexports.src | 3 - src/coreclr/inc/holder.h | 2 - src/coreclr/pal/inc/pal.h | 68 -- src/coreclr/pal/inc/palprivate.h | 14 - src/coreclr/pal/src/CMakeLists.txt | 1 - src/coreclr/pal/src/file/find.cpp | 999 ------------------ src/coreclr/pal/tests/palsuite/CMakeLists.txt | 11 - .../pal/tests/palsuite/compilableTests.txt | 7 - .../tests/palsuite/compileDisabledTests.txt | 2 - .../threadsuspension/mainWrapper.cpp | 273 ----- .../threading/threadsuspension/readme.txt | 11 - .../threadsuspension/threadsuspension.cpp | 906 ---------------- .../mainWrapper.cpp | 273 ----- .../threadsuspension_switchthread/readme.txt | 11 - .../threadsuspension.cpp | 913 ---------------- .../file_io/FindClose/test1/FindClose.cpp | 274 ----- .../FindFirstFileA/test1/FindFirstFileA.cpp | 207 ---- .../FindFirstFileW/test1/FindFirstFileW.cpp | 213 ---- .../FindNextFileA/test1/FindNextFileA.cpp | 242 ----- .../FindNextFileA/test2/findnextfilea.cpp | 106 -- .../FindNextFileW/test1/FindNextFileW.cpp | 246 ----- .../FindNextFileW/test2/findnextfilew.cpp | 106 -- .../file_io/errorpathnotfound/test2/test2.cpp | 254 +---- .../pal/tests/palsuite/paltestlist.txt | 7 - .../palsuite/paltestlist_to_be_reviewed.txt | 2 - src/coreclr/tools/superpmi/mcs/verbmerge.cpp | 177 +++- src/coreclr/tools/superpmi/mcs/verbmerge.h | 66 +- 27 files changed, 197 insertions(+), 5197 deletions(-) delete mode 100644 src/coreclr/pal/src/file/find.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension/mainWrapper.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension/readme.txt delete mode 100644 src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension/threadsuspension.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension_switchthread/mainWrapper.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension_switchthread/readme.txt delete mode 100644 src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension_switchthread/threadsuspension.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/file_io/FindClose/test1/FindClose.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/file_io/FindFirstFileA/test1/FindFirstFileA.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/file_io/FindFirstFileW/test1/FindFirstFileW.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/file_io/FindNextFileA/test1/FindNextFileA.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/file_io/FindNextFileA/test2/findnextfilea.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/file_io/FindNextFileW/test1/FindNextFileW.cpp delete mode 100644 src/coreclr/pal/tests/palsuite/file_io/FindNextFileW/test2/findnextfilew.cpp diff --git a/src/coreclr/dlls/mscordac/mscordac_unixexports.src b/src/coreclr/dlls/mscordac/mscordac_unixexports.src index 05bfd886a7552..0857ba2884f78 100644 --- a/src/coreclr/dlls/mscordac/mscordac_unixexports.src +++ b/src/coreclr/dlls/mscordac/mscordac_unixexports.src @@ -77,9 +77,6 @@ nativeStringResourceTable_mscorrc #DeleteCriticalSection #DuplicateHandle #EnterCriticalSection -#FindClose -#FindFirstFileW -#FindNextFileW #FlushFileBuffers #FlushInstructionCache #FormatMessageW diff --git a/src/coreclr/inc/holder.h b/src/coreclr/inc/holder.h index ea0466df2e16d..47b93d4215fef 100644 --- a/src/coreclr/inc/holder.h +++ b/src/coreclr/inc/holder.h @@ -1115,7 +1115,6 @@ using FieldNuller = SpecializedWrapper<_TYPE, detail::ZeroMem<_TYPE>::Invoke>; FORCEINLINE void VoidCloseHandle(HANDLE h) { if (h != NULL) CloseHandle(h); } // (UINT_PTR) -1 is INVALID_HANDLE_VALUE FORCEINLINE void VoidCloseFileHandle(HANDLE h) { if (h != ((HANDLE)((LONG_PTR) -1))) CloseHandle(h); } -FORCEINLINE void VoidFindClose(HANDLE h) { FindClose(h); } FORCEINLINE void VoidUnmapViewOfFile(void *ptr) { UnmapViewOfFile(ptr); } template @@ -1125,7 +1124,6 @@ FORCEINLINE void TypeUnmapViewOfFile(TYPE *ptr) { UnmapViewOfFile(ptr); } //@TODO: Dangerous default value. Some Win32 functions return INVALID_HANDLE_VALUE, some return NULL (such as CreatEvent). typedef Wrapper, VoidCloseHandle, (UINT_PTR) -1> HandleHolder; typedef Wrapper, VoidCloseFileHandle, (UINT_PTR) -1> FileHandleHolder; -typedef Wrapper, VoidFindClose, (UINT_PTR) -1> FindHandleHolder; typedef Wrapper MapViewHolder; diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h index 48631c96621ec..032b888863ef8 100644 --- a/src/coreclr/pal/inc/pal.h +++ b/src/coreclr/pal/inc/pal.h @@ -541,74 +541,6 @@ CopyFileW( #define CopyFile CopyFileA #endif -typedef struct _WIN32_FIND_DATAA { - DWORD dwFileAttributes; - FILETIME ftCreationTime; - FILETIME ftLastAccessTime; - FILETIME ftLastWriteTime; - DWORD nFileSizeHigh; - DWORD nFileSizeLow; - DWORD dwReserved0; - DWORD dwReserved1; - CHAR cFileName[ MAX_PATH_FNAME ]; - CHAR cAlternateFileName[ 14 ]; -} WIN32_FIND_DATAA, *PWIN32_FIND_DATAA, *LPWIN32_FIND_DATAA; - -typedef struct _WIN32_FIND_DATAW { - DWORD dwFileAttributes; - FILETIME ftCreationTime; - FILETIME ftLastAccessTime; - FILETIME ftLastWriteTime; - DWORD nFileSizeHigh; - DWORD nFileSizeLow; - DWORD dwReserved0; - DWORD dwReserved1; - WCHAR cFileName[ MAX_PATH_FNAME ]; - WCHAR cAlternateFileName[ 14 ]; -} WIN32_FIND_DATAW, *PWIN32_FIND_DATAW, *LPWIN32_FIND_DATAW; - -#ifdef UNICODE -typedef WIN32_FIND_DATAW WIN32_FIND_DATA; -typedef PWIN32_FIND_DATAW PWIN32_FIND_DATA; -typedef LPWIN32_FIND_DATAW LPWIN32_FIND_DATA; -#else -typedef WIN32_FIND_DATAA WIN32_FIND_DATA; -typedef PWIN32_FIND_DATAA PWIN32_FIND_DATA; -typedef LPWIN32_FIND_DATAA LPWIN32_FIND_DATA; -#endif - -PALIMPORT -HANDLE -PALAPI -FindFirstFileW( - IN LPCWSTR lpFileName, - OUT LPWIN32_FIND_DATAW lpFindFileData); - -#ifdef UNICODE -#define FindFirstFile FindFirstFileW -#else -#define FindFirstFile FindFirstFileA -#endif - -PALIMPORT -BOOL -PALAPI -FindNextFileW( - IN HANDLE hFindFile, - OUT LPWIN32_FIND_DATAW lpFindFileData); - -#ifdef UNICODE -#define FindNextFile FindNextFileW -#else -#define FindNextFile FindNextFileA -#endif - -PALIMPORT -BOOL -PALAPI -FindClose( - IN OUT HANDLE hFindFile); - PALIMPORT DWORD PALAPI diff --git a/src/coreclr/pal/inc/palprivate.h b/src/coreclr/pal/inc/palprivate.h index 35a05cec3b908..eff6820558595 100644 --- a/src/coreclr/pal/inc/palprivate.h +++ b/src/coreclr/pal/inc/palprivate.h @@ -49,20 +49,6 @@ CreateDirectoryW( IN LPCWSTR lpPathName, IN LPSECURITY_ATTRIBUTES lpSecurityAttributes); -PALIMPORT -HANDLE -PALAPI -FindFirstFileA( - IN LPCSTR lpFileName, - OUT LPWIN32_FIND_DATAA lpFindFileData); - -PALIMPORT -BOOL -PALAPI -FindNextFileA( - IN HANDLE hFindFile, - OUT LPWIN32_FIND_DATAA lpFindFileData); - PALIMPORT DWORD PALAPI diff --git a/src/coreclr/pal/src/CMakeLists.txt b/src/coreclr/pal/src/CMakeLists.txt index de0d836ad4ab0..07c81a0661352 100644 --- a/src/coreclr/pal/src/CMakeLists.txt +++ b/src/coreclr/pal/src/CMakeLists.txt @@ -142,7 +142,6 @@ set(SOURCES file/directory.cpp file/file.cpp file/filetime.cpp - file/find.cpp file/path.cpp handlemgr/handleapi.cpp handlemgr/handlemgr.cpp diff --git a/src/coreclr/pal/src/file/find.cpp b/src/coreclr/pal/src/file/find.cpp deleted file mode 100644 index e0717970cf547..0000000000000 --- a/src/coreclr/pal/src/file/find.cpp +++ /dev/null @@ -1,999 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*++ - - - -Module Name: - - find.c - -Abstract: - - Implementation of the FindFile function family - -Revision History: - - - ---*/ - -#include "pal/thread.hpp" -#include "pal/file.hpp" -#include "pal/stackstring.hpp" - -#include "pal/palinternal.h" -#include "pal/dbgmsg.h" -#include "pal/file.h" -#include "pal/filetime.h" - -#include -#include -#include -#include - -using namespace CorUnix; - -SET_DEFAULT_DEBUG_CHANNEL(FILE); - -namespace CorUnix -{ - int InternalGlob( - const char *szPattern, - int nFlags, -#if ERROR_FUNC_FOR_GLOB_HAS_FIXED_PARAMS - int (*pnErrFunc)(const char *, int), -#else - int (*pnErrFunc)(...), -#endif - glob_t *pgGlob - ); - - /*++ - InternalGlob - - Input parameters: - - szPattern = pointer to a pathname pattern to be expanded - nFlags = arguments to modify the behavior of glob - pnErrFunc = pointer to a routine that handles errors during the glob call - pgGlob = pointer to a glob structure - - Return value: - 0 on success, -1 on failure. - - Some platforms expect the error function for glob to take a variable number - of parameters, whereas other platforms insist that the error function take - a const char * and an int. A test in configure determines which is the case - for each platform and sets ERROR_FUNC_FOR_GLOB_HAS_FIXED_PARAMS - to 1 if the error func must have the char * and int parameters. - --*/ - int - InternalGlob( - const char *szPattern, - int nFlags, -#if ERROR_FUNC_FOR_GLOB_HAS_FIXED_PARAMS - int (*pnErrFunc)(const char *, int), -#else - int (*pnErrFunc)(...), -#endif - glob_t *pgGlob - ) - { - int nRet = -1; - nRet = glob(szPattern, nFlags, pnErrFunc, pgGlob); - return nRet; - } -} - -static BOOL FILEDosGlobA( - CPalThread *pthrCurrent, - const char *pattern, - int flags, - glob_t *pgGlob ); - -static int FILEGlobQsortCompare(const void *in_str1, const void *in_str2); - -static int FILEGlobFromSplitPath( - const char *dir, - const char *fname, - const char *ext, - int flags, - glob_t *pgGlob ); - -/*++ -Function: - FindFirstFileA - -See MSDN doc. ---*/ -HANDLE -PALAPI -FindFirstFileA( - IN LPCSTR lpFileName, - OUT LPWIN32_FIND_DATAA lpFindFileData) -{ - HANDLE hRet = INVALID_HANDLE_VALUE; - DWORD dwLastError = NO_ERROR; - find_obj *find_data = NULL; - CPalThread *pthrCurrent = InternalGetCurrentThread(); - - PERF_ENTRY(FindFirstFileA); - ENTRY("FindFirstFileA(lpFileName=%p (%s), lpFindFileData=%p)\n", - lpFileName?lpFileName:"NULL", - lpFileName?lpFileName:"NULL", lpFindFileData); - - if(NULL == lpFileName) - { - ERROR("lpFileName is NULL!\n"); - dwLastError = ERROR_PATH_NOT_FOUND; - goto done; - } - if(NULL == lpFindFileData) - { - ASSERT("lpFindFileData is NULL!\n"); - dwLastError = ERROR_INVALID_PARAMETER; - goto done; - } - - find_data = (find_obj *)malloc(sizeof(find_obj)); - if ( find_data == NULL ) - { - ERROR("Unable to allocate memory for find_data\n"); - dwLastError = ERROR_NOT_ENOUGH_MEMORY; - goto done; - } - - find_data->self_addr = find_data; - - // Clear the glob_t so we can safely call globfree() on it - // regardless of whether FILEDosGlobA ends up calling glob(). - memset(&(find_data->gGlob), 0, sizeof(find_data->gGlob)); - - if (!FILEDosGlobA(pthrCurrent, lpFileName, 0, &(find_data->gGlob))) - { - // FILEDosGlobA will call SetLastError() on failure. - goto done; - } - else - { - // Check if there's at least one match. - if (find_data->gGlob.gl_pathc == 0) - { - /* Testing has indicated that for this API the - * last errors are as follows - * c:\temp\foo.txt - no error - * c:\temp\foo - ERROR_FILE_NOT_FOUND - * c:\temp\foo\bar - ERROR_PATH_NOT_FOUND - * c:\temp\foo.txt\bar - ERROR_DIRECTORY - * - */ - LPSTR lpTemp = strdup((LPSTR)lpFileName); - if ( !lpTemp ) - { - ERROR( "strdup failed!\n" ); - SetLastError( ERROR_INTERNAL_ERROR ); - goto done; - } - FILEGetProperNotFoundError( lpTemp, &dwLastError ); - - if ( ERROR_PATH_NOT_FOUND == dwLastError ) - { - /* If stripping the last segment reveals a file name then - the error is ERROR_DIRECTORY. */ - struct stat stat_data; - LPSTR lpLastPathSeparator = NULL; - - lpLastPathSeparator = strrchr( lpTemp, '/'); - - if ( lpLastPathSeparator != NULL ) - { - *lpLastPathSeparator = '\0'; - - if ( stat( lpTemp, &stat_data) == 0 && - (stat_data.st_mode & S_IFMT) == S_IFREG ) - { - dwLastError = ERROR_DIRECTORY; - } - } - } - free(lpTemp); - lpTemp = NULL; - goto done; - } - - find_data->next = find_data->gGlob.gl_pathv; - } - - if ( FindNextFileA( (HANDLE)find_data, lpFindFileData ) ) - { - hRet = (HANDLE)find_data; - } - -done: - - if ( hRet == INVALID_HANDLE_VALUE ) - { - if(NULL != find_data) - { - // Call globfree only when there is any pattern match - // otherwise, HPUX C library segfaults. - if (NULL != find_data->gGlob.gl_pathv) - { - globfree( &(find_data->gGlob) ); - } - free(find_data); - } - if (dwLastError) - { - SetLastError(dwLastError); - } - } - - LOGEXIT("FindFirstFileA returns HANDLE %p\n", hRet ); - PERF_EXIT(FindFirstFileA); - return hRet; -} - - -/*++ -Function: - FindFirstFileW - -See MSDN doc. ---*/ -HANDLE -PALAPI -FindFirstFileW( - IN LPCWSTR lpFileName, - OUT LPWIN32_FIND_DATAW lpFindFileData) -{ - // MAX_PATH_FNAME in this context is a file name, not a full path to a file. - HANDLE retval = INVALID_HANDLE_VALUE; - CHAR FileNameA[MAX_PATH_FNAME]; - WIN32_FIND_DATAA FindFileDataA; - - PERF_ENTRY(FindFirstFileW); - ENTRY("FindFirstFileW(lpFileName=%p (%S), lpFindFileData=%p)\n", - lpFileName?lpFileName:W16_NULLSTRING, - lpFileName?lpFileName:W16_NULLSTRING, lpFindFileData); - - if(NULL == lpFileName) - { - ERROR("lpFileName is NULL!\n"); - SetLastError(ERROR_PATH_NOT_FOUND); - goto done; - } - - if(NULL == lpFindFileData) - { - ERROR("lpFindFileData is NULL!\n"); - SetLastError(ERROR_INVALID_PARAMETER); - goto done; - } - if( 0 == WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, lpFileName, -1, - FileNameA, MAX_PATH_FNAME, NULL, NULL)) - { - DWORD dwLastError = GetLastError(); - if (dwLastError == ERROR_INSUFFICIENT_BUFFER) - { - WARN("lpFileName is larger than MAX_PATH_FNAME (%d)!\n", MAX_PATH_FNAME); - SetLastError(ERROR_FILENAME_EXCED_RANGE); - } - else - { - ASSERT("WideCharToMultiByte failed! error is %d\n", dwLastError); - SetLastError(ERROR_INTERNAL_ERROR); - } - goto done; - } - - retval = FindFirstFileA(FileNameA, &FindFileDataA); - if( INVALID_HANDLE_VALUE == retval ) - { - TRACE("FindFirstFileA failed!\n"); - goto done; - } - - lpFindFileData->dwFileAttributes = FindFileDataA.dwFileAttributes; - lpFindFileData->dwReserved0 = FindFileDataA.dwReserved0; - lpFindFileData->dwReserved1 = FindFileDataA.dwReserved1; - lpFindFileData->ftCreationTime = FindFileDataA.ftCreationTime; - lpFindFileData->ftLastAccessTime = FindFileDataA.ftLastAccessTime; - lpFindFileData->ftLastWriteTime = FindFileDataA.ftLastWriteTime; - lpFindFileData->nFileSizeHigh = FindFileDataA.nFileSizeHigh; - lpFindFileData->nFileSizeLow = FindFileDataA.nFileSizeLow; - - /* no 8.3 file names */ - lpFindFileData->cAlternateFileName[0] = 0; - - if( 0 == MultiByteToWideChar(CP_ACP, 0, FindFileDataA.cFileName, -1, - lpFindFileData->cFileName, MAX_PATH_FNAME)) - { - DWORD dwLastError = GetLastError(); - if (dwLastError == ERROR_INSUFFICIENT_BUFFER) - { - WARN("FindFileDataA.cFileName is larger than MAX_PATH_FNAME (%d)!\n", MAX_PATH_FNAME); - SetLastError(ERROR_FILENAME_EXCED_RANGE); - } - else - { - ASSERT("MultiByteToWideChar failed! error is %d\n", dwLastError); - SetLastError(ERROR_INTERNAL_ERROR); - } - FindClose(retval); - retval = INVALID_HANDLE_VALUE; - } -done: - LOGEXIT("FindFirstFileW returns HANDLE %p\n", retval); - PERF_EXIT(FindFirstFileW); - return retval; -} - - -/*++ -Function: - FindNextFileA - -See MSDN doc. ---*/ -BOOL -PALAPI -FindNextFileA( - IN HANDLE hFindFile, - OUT LPWIN32_FIND_DATAA lpFindFileData) -{ - find_obj *find_data; - - BOOL bRet = FALSE; - DWORD dwLastError = 0; - DWORD Attr; - - PERF_ENTRY(FindNextFileA); - ENTRY("FindNextFileA(hFindFile=%p, lpFindFileData=%p)\n", - hFindFile, lpFindFileData); - - find_data = (find_obj*)hFindFile; - - if ( hFindFile == INVALID_HANDLE_VALUE || - find_data == NULL || - find_data->self_addr != find_data ) - { - TRACE("FindNextFileA received an invalid handle\n"); - dwLastError = ERROR_INVALID_HANDLE; - goto done; - } - - if ( find_data->next) - { - struct stat stat_data; - char ext[_MAX_EXT]; - int stat_result; - - while (*(find_data->next)) - { - char *path = *(find_data->next); - - TRACE("Found [%s]\n", path); - - // Split the path into a dir and filename. - if (_splitpath_s(path, NULL, 0, find_data->dir, _MAX_DIR, find_data->fname, _MAX_PATH, ext, _MAX_EXT) != 0) - { - ASSERT("_splitpath_s failed on %s\n", path); - dwLastError = ERROR_INTERNAL_ERROR; - goto done; - } - strcat_s( find_data->fname, sizeof(find_data->fname), ext ); - - /* get the attributes, but continue if it fails */ - Attr = GetFileAttributesA(path); - if (Attr == INVALID_FILE_ATTRIBUTES) - { - WARN("GetFileAttributes returned -1 on file [%s]\n", - *(find_data->next)); - } - lpFindFileData->dwFileAttributes = Attr; - - /* Note that cFileName is NOT the relative path */ - if (strcpy_s( lpFindFileData->cFileName, sizeof(lpFindFileData->cFileName), find_data->fname ) != SAFECRT_SUCCESS) - { - TRACE("strcpy_s failed!\n"); - dwLastError = ERROR_FILENAME_EXCED_RANGE; - goto done; - } - - /* we don't support 8.3 filenames, so just leave it empty */ - lpFindFileData->cAlternateFileName[0] = 0; - - /* get the filetimes */ - stat_result = stat(path, &stat_data) == 0 || - lstat(path, &stat_data) == 0; - - find_data->next++; - - if ( stat_result ) - { - lpFindFileData->ftCreationTime = - FILEUnixTimeToFileTime( stat_data.st_ctime, - ST_CTIME_NSEC(&stat_data) ); - lpFindFileData->ftLastAccessTime = - FILEUnixTimeToFileTime( stat_data.st_atime, - ST_ATIME_NSEC(&stat_data) ); - lpFindFileData->ftLastWriteTime = - FILEUnixTimeToFileTime( stat_data.st_mtime, - ST_MTIME_NSEC(&stat_data) ); - - /* if Unix mtime is greater than atime, return mtime - as the last access time */ - if (CompareFileTime(&lpFindFileData->ftLastAccessTime, - &lpFindFileData->ftLastWriteTime) < 0) - { - lpFindFileData->ftLastAccessTime = lpFindFileData->ftLastWriteTime; - } - - /* if Unix ctime is greater than mtime, return mtime - as the create time */ - if (CompareFileTime(&lpFindFileData->ftLastWriteTime, - &lpFindFileData->ftCreationTime) < 0) - { - lpFindFileData->ftCreationTime = lpFindFileData->ftLastWriteTime; - } - - /* get file size */ - lpFindFileData->nFileSizeLow = (DWORD)stat_data.st_size; - #if SIZEOF_OFF_T > 4 - lpFindFileData->nFileSizeHigh = - (DWORD)(stat_data.st_size >> 32); - #else - lpFindFileData->nFileSizeHigh = 0; - #endif - - bRet = TRUE; - break; - } - } - if(!bRet) - { - dwLastError = ERROR_NO_MORE_FILES; - } - } - else - { - - ASSERT("find_data->next is (mysteriously) NULL\n"); - } - -done: - if (dwLastError) - { - SetLastError(dwLastError); - } - - LOGEXIT("FindNextFileA returns BOOL %d\n", bRet); - PERF_EXIT(FindNextFileA); - return bRet; -} - - -/*++ -Function: - FindNextFileW - -See MSDN doc. ---*/ -BOOL -PALAPI -FindNextFileW( - IN HANDLE hFindFile, - OUT LPWIN32_FIND_DATAW lpFindFileData) -{ - BOOL retval = FALSE; - WIN32_FIND_DATAA FindFileDataA; - - PERF_ENTRY(FindNextFileW); - ENTRY("FindNextFileW(hFindFile=%p, lpFindFileData=%p)\n", - hFindFile, lpFindFileData); - - retval = FindNextFileA(hFindFile, &FindFileDataA); - if(!retval) - { - WARN("FindNextFileA failed!\n"); - goto done; - } - - lpFindFileData->dwFileAttributes = FindFileDataA.dwFileAttributes; - lpFindFileData->dwReserved0 = FindFileDataA.dwReserved0; - lpFindFileData->dwReserved1 = FindFileDataA.dwReserved1; - lpFindFileData->ftCreationTime = FindFileDataA.ftCreationTime; - lpFindFileData->ftLastAccessTime = FindFileDataA.ftLastAccessTime; - lpFindFileData->ftLastWriteTime = FindFileDataA.ftLastWriteTime; - lpFindFileData->nFileSizeHigh = FindFileDataA.nFileSizeHigh; - lpFindFileData->nFileSizeLow = FindFileDataA.nFileSizeLow; - - /* no 8.3 file names */ - lpFindFileData->cAlternateFileName[0] = 0; - - if( 0 == MultiByteToWideChar(CP_ACP, 0, FindFileDataA.cFileName, -1, - lpFindFileData->cFileName, MAX_PATH_FNAME)) - { - DWORD dwLastError = GetLastError(); - if (dwLastError == ERROR_INSUFFICIENT_BUFFER) - { - WARN("FindFileDataA.cFileName is larger than MAX_PATH_FNAME (%d)!\n", MAX_PATH_FNAME); - SetLastError(ERROR_FILENAME_EXCED_RANGE); - } - else - { - ASSERT("MultiByteToWideChar failed! error is %d\n", dwLastError); - SetLastError(ERROR_INTERNAL_ERROR); - } - retval = FALSE; - } - -done: - LOGEXIT("FindNextFileW returns BOOL %d\n", retval); - PERF_EXIT(FindNextFileW); - return retval; -} - - -/*++ -Function: - FindClose - -See MSDN doc. ---*/ -BOOL -PALAPI -FindClose( - IN OUT HANDLE hFindFile) -{ - find_obj *find_data; - BOOL hRet = TRUE; - DWORD dwLastError = 0; - - PERF_ENTRY(FindClose); - ENTRY("FindClose(hFindFile=%p)\n", hFindFile); - - find_data = (find_obj*)hFindFile; - - if ( hFindFile == INVALID_HANDLE_VALUE || - find_data == NULL || - find_data->self_addr != find_data ) - { - ERROR("Invalid find handle\n"); - hRet = FALSE; - dwLastError = ERROR_INVALID_PARAMETER; - goto done; - } - - find_data->self_addr = NULL; - - // Call globfree only when there is any pattern match - // otherwise, HPUX C library segfaults. - if (NULL != find_data->gGlob.gl_pathv) - { - globfree( &(find_data->gGlob) ); - } - free(find_data); - -done: - if (dwLastError) - { - SetLastError(dwLastError); - } - - LOGEXIT("FindClose returns BOOL %d\n", hRet); - PERF_EXIT(FindClose); - return hRet; -} - - -/*++ -Function: - FILEMakePathA - -Mimics _makepath from windows, except it's a bit safer. -Any or all of dir, fname, and ext can be NULL. ---*/ -static int FILEMakePathA( char *buff, - int buff_size, - const char *dir, - const char *fname, - const char *ext ) -{ - int dir_len = 0; - int fname_len = 0; - int ext_len = 0; - int len; - char *p; - - TRACE("Attempting to assemble path from [%s][%s][%s], buff_size = %d\n", - dir?dir:"NULL", fname?fname:"NULL", ext?ext:"NULL", buff_size); - - if (dir) dir_len = strlen(dir); - if (fname) fname_len = strlen(fname); - if (ext) ext_len = strlen(ext); - - len = dir_len + fname_len + ext_len + 1; - - TRACE("Required buffer size is %d bytes\n", len); - - if ( len > buff_size ) - { - ERROR("Buffer is too small (%d bytes), needs %d bytes\n", - buff_size, len); - return -1; - } - else - { - buff[0] = 0; - - p = buff; - if (dir_len > 0) - { - if (strncpy_s( buff, buff_size, dir, dir_len + 1 ) != SAFECRT_SUCCESS) - { - ERROR("FILEMakePathA: strncpy_s failed\n"); - return -1; - } - - p += dir_len; - buff_size-= dir_len; - } - if (fname_len > 0) - { - if (strncpy_s( p, buff_size, fname, fname_len + 1 ) != SAFECRT_SUCCESS) - { - ERROR("FILEMakePathA: strncpy_s failed\n"); - return -1; - } - - p += fname_len; - buff_size-=fname_len; - } - if (ext_len > 0) - { - if (strncpy_s( p, buff_size, ext, ext_len + 1) != SAFECRT_SUCCESS) - { - ERROR("FILEMakePathA: strncpy_s failed\n"); - return -1; - } - } - - TRACE("FILEMakePathA assembled [%s]\n", buff); - return len - 1; - } -} - - -/*++ - FILEGlobQsortCompare - - Comparison function required by qsort, so that the - . and .. directories end up on top of the sorted list - of directories. ---*/ -static int FILEGlobQsortCompare(const void *in_str1, const void *in_str2) -{ - char **str1 = (char**)in_str1; - char **str2 = (char**)in_str2; - const int FIRST_ARG_LESS = -1; - const int FIRST_ARG_EQUAL = 0; - const int FIRST_ARG_GREATER = 1; - - /* If both strings are equal, return immediately */ - if (strcmp(*(str1), *(str2)) == 0) - { - return(FIRST_ARG_EQUAL); - } - - /* Have '.' always on top than any other search result */ - if (strcmp(*(str1), ".") == 0) - { - return (FIRST_ARG_LESS); - } - if (strcmp(*(str2), ".") == 0) - { - return (FIRST_ARG_GREATER); - } - - /* Have '..' next on top, over any other search result */ - if (strcmp(*(str1), "..") == 0) - { - return (FIRST_ARG_LESS); - } - if (strcmp(*(str2), "..") == 0) - { - return (FIRST_ARG_GREATER); - } - - /* Finally, let strcmp do the rest for us */ - return (strcmp(*(str1),*(str2))); -} - -/*++ -Function: - FILEEscapeSquareBrackets - -Simple helper function to insert backslashes before square brackets -to prevent glob from using them as wildcards. - -note: this functions assumes all backslashes have previously been - converted into forwardslashes by _splitpath_s. ---*/ -static void FILEEscapeSquareBrackets(char *pattern, char *escaped_pattern) -{ - TRACE("Entering FILEEscapeSquareBrackets: [%p (%s)][%p]\n", - pattern,pattern,escaped_pattern); - -#if _ENABLE_DEBUG_MESSAGES_ - char *escaped_pattern_base = escaped_pattern; -#endif // _ENABLE_DEBUG_MESSAGES_ - - while(*pattern) - { - if('[' == *pattern || ']' == *pattern) - { - *escaped_pattern = '\\'; - escaped_pattern++; - } - *escaped_pattern = *pattern; - pattern++; - escaped_pattern++; - } - *escaped_pattern='\0'; - - TRACE("FILEEscapeSquareBrackets done. escaped_pattern=%s\n", - escaped_pattern_base); -} - - -/*++ -Function: - FILEGlobFromSplitPath - -Simple wrapper function around glob(3), except that the pattern is accepted -in broken-down form like _splitpath_s produces. - -ie. calling splitpath on a pattern then calling this function should -produce the same result as just calling glob() on the pattern. ---*/ -static int FILEGlobFromSplitPath( const char *dir, - const char *fname, - const char *ext, - int flags, - glob_t *pgGlob ) -{ - int Ret; - PathCharString PatternPS; - PathCharString EscapedPatternPS; - char * Pattern; - int length = 0; - char * EscapedPattern; - - TRACE("We shall attempt to glob from components [%s][%s][%s]\n", - dir?dir:"NULL", fname?fname:"NULL", ext?ext:"NULL"); - - if (dir) length = strlen(dir); - if (fname) length += strlen(fname); - if (ext) length += strlen(ext); - - Pattern = PatternPS.OpenStringBuffer(length); - if (NULL == Pattern) - { - ERROR("Not Enough memory."); - return -1; - } - FILEMakePathA( Pattern, length+1, dir, fname, ext ); - PatternPS.CloseBuffer(length); - TRACE("Assembled Pattern = [%s]\n", Pattern); - - /* special handling is needed to handle the case where - filename contains '[' and ']' */ - EscapedPattern = EscapedPatternPS.OpenStringBuffer(length*2); - if (NULL == EscapedPattern) - { - ERROR("Not Enough memory."); - return -1; - } - FILEEscapeSquareBrackets( Pattern, EscapedPattern); - EscapedPatternPS.CloseBuffer(strlen(EscapedPattern)); -#ifdef GLOB_QUOTE - flags |= GLOB_QUOTE; -#endif // GLOB_QUOTE - Ret = InternalGlob(EscapedPattern, flags, NULL, pgGlob); - -#ifdef GLOB_NOMATCH - if (Ret == GLOB_NOMATCH) - { - // pgGlob->gl_pathc will be 0 in this case. We'll check - // the return value to see if an error occurred, so we - // don't want to return an error if we simply didn't match - // anything. - Ret = 0; - } -#endif // GLOB_NOMATCH - - /* Ensure that . and .. are placed in front, and sort the rest */ - qsort(pgGlob->gl_pathv, pgGlob->gl_pathc, sizeof(char*), - FILEGlobQsortCompare); - TRACE("Result of glob() is %d\n", Ret); - - return Ret; -} - - -/*++ -Function: - FILEDosGlobA - -Generate pathnames matching a DOS globbing pattern. This function has a similar -prototype to glob(3), and fulfils the same purpose. However, DOS globbing -is slightly different than Unix in the following ways: - -- '.*' at the end of a pattern means "any file extension, or none at all", -whereas Unix has no concept of file extensions, and will match the '.' like -any other character - -- on Unix, filenames beginning with '.' must be explicitly matched. This is -not true in DOS - -- in DOS, the first two entries (if they match) will be '.' and '..', followed -by all other matching entries sorted in ASCII order. In Unix, all entries are -treated equally, so '+file' would appear before '.' and '..' - -- DOS globbing will fail if any wildcard characters occur before the last path -separator - -This implementation of glob implements the DOS behavior in all these cases, -but otherwise attempts to behave exactly like POSIX glob. The only exception -is its return value -- it returns TRUE if it succeeded (finding matches or -finding no matches but without any error occurring) or FALSE if any error -occurs. It calls SetLastError() if it returns FALSE. - -Sorting doesn't seem to be consistent on all Windows platform, and it's -not required for CoreCLR to have the same sorting algorithm as Windows 2000. -This implementation will give slightly different result for the sort list -than Windows 2000. - ---*/ -static BOOL FILEDosGlobA( CPalThread *pthrCurrent, - const char *pattern, - int flags, - glob_t *pgGlob ) -{ - char Dir[_MAX_DIR]; - char FilenameBuff[_MAX_FNAME + 1]; - char *Filename = FilenameBuff + 1; - char Ext[_MAX_EXT]; - int A, B, C; - BOOL result = TRUE; - int globResult = 0; - - Dir[0] = 0; - FilenameBuff[0] = '.'; - FilenameBuff[1] = 0; - Ext[0] = 0; - - _splitpath_s( pattern, NULL, 0, Dir, _MAX_DIR, Filename, _MAX_FNAME+1, Ext, _MAX_EXT); - - /* check to see if _splitpath_s failed */ - if ( Filename[0] == 0 ) - { - if ( Dir[0] == 0 ) - { - ERROR("_splitpath_s failed on path [%s]\n", pattern); - } - else - { - ERROR("Pattern contains a trailing backslash\n"); - } - SetLastError(ERROR_PATH_NOT_FOUND); - result = FALSE; - goto done; - } - - TRACE("glob pattern [%s] split into [%s][%s][%s]\n", - pattern, Dir, Filename, Ext); - - if ( strchr(Dir, '*') != NULL || strchr(Dir, '?') != NULL ) - { - ERROR("Found wildcard character(s) ('*' and/or '?') before " - "last path separator\n"); - SetLastError(ERROR_PATH_NOT_FOUND); - result = FALSE; - goto done; - } - - /* The meat of the routine happens below. Basically, there are three - special things to check for: - - (A) If the extension is _exactly_ '.*', we will need to do two globs, - one for 'filename.*' and one for 'filename', EXCEPT if (B) the last - character of filename is '*', in which case we can eliminate the - extension altogether, since '*.*' and '*' are the same in DOS. - (C) If the first character of the filename is '*', we need to do - an additional glob for each one we have already done, except with - '.' prepended to the filename of the patterns, because in Unix, - hidden files need to be matched explicitly. - - We can ignore the extension by calling FILEGlobFromSplitPath with - the extension parameter as "", and we can prepend '.' to the - filename by using (Filename - 1), since Filename conveniently points - to the second character of a buffer which happens to have '.' as - its first character. - */ - - A = strncmp(Ext, ".*", 3) == 0; - B = (Filename[strlen(Filename) - 1] == '*'); - C = (*Filename == '*'); - - TRACE("Extension IS%s '.*', filename DOES%s end with '*', " - "and filename DOES%s begin with '*'\n", - A?"":" NOT", B?"":" NOT", C?"":" NOT"); - - if ( !(A && B) ) - { - /* the original pattern */ - globResult = FILEGlobFromSplitPath(Dir, Filename, Ext, 0, pgGlob); - if ( globResult != 0 ) - { - goto done; - } - - if (C) - { - /* the original pattern but '.' prepended to filename */ - globResult = FILEGlobFromSplitPath(Dir, Filename - 1, Ext, - GLOB_APPEND, pgGlob); - if ( globResult != 0 ) - { - goto done; - } - } - } - - if (A) - { - /* if (A && B), this is the first glob() call. The first call - to glob must use flags = 0, while proceeding calls should - set the GLOB_APPEND flag. */ - globResult = FILEGlobFromSplitPath(Dir, Filename, "", - (A && B)?0:GLOB_APPEND, pgGlob); - if ( globResult != 0 ) - { - goto done; - } - - if (C) - { - /* omit the extension and prepend '.' to filename */ - globResult = FILEGlobFromSplitPath(Dir, Filename - 1, "", - GLOB_APPEND, pgGlob); - if ( globResult != 0 ) - { - goto done; - } - } - } - -done: - if (globResult != 0) - { - if (globResult == GLOB_NOSPACE) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - } - else - { - SetLastError(ERROR_INTERNAL_ERROR); - } - result = FALSE; - } - TRACE("Returning %d\n", result); - return result; -} - - diff --git a/src/coreclr/pal/tests/palsuite/CMakeLists.txt b/src/coreclr/pal/tests/palsuite/CMakeLists.txt index 5c65aa5c52fae..cc0fec6d2aa70 100644 --- a/src/coreclr/pal/tests/palsuite/CMakeLists.txt +++ b/src/coreclr/pal/tests/palsuite/CMakeLists.txt @@ -57,10 +57,6 @@ add_executable_clr(paltests #composite/synchronization/nativecs_interlocked/mtx_critsect.cpp #composite/synchronization/nativecs_interlocked/pal_composite_native_cs.cpp #composite/synchronization/nativecs_interlocked/resultbuffer.cpp - #composite/threading/threadsuspension/mainWrapper.cpp - #composite/threading/threadsuspension/threadsuspension.cpp - #composite/threading/threadsuspension_switchthread/mainWrapper.cpp - #composite/threading/threadsuspension_switchthread/threadsuspension.cpp #composite/wfmo/main.cpp #composite/wfmo/mutex.cpp c_runtime/atof/test1/test1.cpp @@ -273,13 +269,6 @@ add_executable_clr(paltests file_io/errorpathnotfound/test1/test1.cpp file_io/errorpathnotfound/test2/test2.cpp file_io/FILECanonicalizePath/FILECanonicalizePath.cpp - file_io/FindClose/test1/FindClose.cpp - file_io/FindFirstFileA/test1/FindFirstFileA.cpp - file_io/FindFirstFileW/test1/FindFirstFileW.cpp - file_io/FindNextFileA/test1/FindNextFileA.cpp - file_io/FindNextFileA/test2/findnextfilea.cpp - file_io/FindNextFileW/test1/FindNextFileW.cpp - file_io/FindNextFileW/test2/findnextfilew.cpp file_io/FlushFileBuffers/test1/FlushFileBuffers.cpp file_io/GetConsoleOutputCP/test1/GetConsoleOutputCP.cpp file_io/GetFileAttributesA/test1/GetFileAttributesA.cpp diff --git a/src/coreclr/pal/tests/palsuite/compilableTests.txt b/src/coreclr/pal/tests/palsuite/compilableTests.txt index 593faa173f1bf..eec58858f3b0d 100644 --- a/src/coreclr/pal/tests/palsuite/compilableTests.txt +++ b/src/coreclr/pal/tests/palsuite/compilableTests.txt @@ -193,13 +193,6 @@ file_io/CreateFileW/test1/paltest_createfilew_test1 file_io/errorpathnotfound/test1/paltest_errorpathnotfound_test1 file_io/errorpathnotfound/test2/paltest_errorpathnotfound_test2 file_io/FILECanonicalizePath/paltest_filecanonicalizepath_test1 -file_io/FindClose/test1/paltest_findclose_test1 -file_io/FindFirstFileA/test1/paltest_findfirstfilea_test1 -file_io/FindFirstFileW/test1/paltest_findfirstfilew_test1 -file_io/FindNextFileA/test1/paltest_findnextfilea_test1 -file_io/FindNextFileA/test2/paltest_findnextfilea_test2 -file_io/FindNextFileW/test1/paltest_findnextfilew_test1 -file_io/FindNextFileW/test2/paltest_findnextfilew_test2 file_io/FlushFileBuffers/test1/paltest_flushfilebuffers_test1 file_io/GetConsoleOutputCP/test1/paltest_getconsoleoutputcp_test1 file_io/GetFileAttributesA/test1/paltest_getfileattributesa_test1 diff --git a/src/coreclr/pal/tests/palsuite/compileDisabledTests.txt b/src/coreclr/pal/tests/palsuite/compileDisabledTests.txt index 1a14daf4564a4..4ca3275daaeb4 100644 --- a/src/coreclr/pal/tests/palsuite/compileDisabledTests.txt +++ b/src/coreclr/pal/tests/palsuite/compileDisabledTests.txt @@ -7,8 +7,6 @@ composite/object_management/semaphore/shared/paltest_semaphore_shared composite/synchronization/criticalsection/paltest_synchronization_criticalsection composite/synchronization/nativecriticalsection/paltest_synchronization_nativecriticalsection composite/synchronization/nativecs_interlocked/paltest_synchronization_nativecs_interlocked -composite/threading/threadsuspension/paltest_threading_threadsuspension -composite/threading/threadsuspension_switchthread/paltest_threading_threadsuspension_switchthread composite/wfmo/paltest_composite_wfmo c_runtime/iswprint/test1/paltest_iswprint_test1 c_runtime/vprintf/test1/paltest_vprintf_test1 diff --git a/src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension/mainWrapper.cpp b/src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension/mainWrapper.cpp deleted file mode 100644 index 24cb27d108509..0000000000000 --- a/src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension/mainWrapper.cpp +++ /dev/null @@ -1,273 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/* -Source Code: mainWrapper.c - -mainWrapper.c creates Composite Test Case Processes and waits for all processes to get over - -Algorithm -o Create PROCESS_COUNT processes. - -Author: RameshG -*/ - -#include -#include "resulttime.h" - -/* Test Input Variables */ -unsigned int USE_PROCESS_COUNT = 0; //default -unsigned int WORKER_THREAD_MULTIPLIER_COUNT = 0; //default -unsigned int REPEAT_COUNT = 0; //default -unsigned int SLEEP_LENGTH = 0; //default -unsigned int RELATION_ID = 0;//default -unsigned int THREAD_COUNT = 1; //There is only one suspender and resume thread for this test case - -char *testCaseName; - - -struct applicationStatistics{ - DWORD operationTime; - unsigned int relationId; - unsigned int processCount; - unsigned int threadCount; - unsigned int repeatCount; - char* buildNumber; - -}; - - -int GetParameters( int argc, char **argv) -{ - - if( (argc != 5) || ((argc == 1) && !strcmp(argv[1],"/?")) - || !strcmp(argv[1],"/h") || !strcmp(argv[1],"/H")) - { - printf("PAL -Composite Thread Suspension Test\n"); - printf("Usage:\n"); - printf("\t[PROCESS_COUNT] Greater than or Equal to 1 \n"); - printf("\t[WORKER_THREAD_MULTIPLIER_COUNT] Greater than or Equal to 1 and Less than or Equal to %d \n", MAXIMUM_WAIT_OBJECTS); - printf("\t[REPEAT_COUNT] Greater than or Equal to 1\n"); - printf("\t[RELATION_ID [greater than or Equal to 1]\n"); - return -1; - } - - // Trace("Args 1 is [%s], Arg 2 is [%s], Arg 3 is [%s]\n", argv[1], argv[2], argv[3]); - - USE_PROCESS_COUNT = atoi(argv[1]); - if( USE_PROCESS_COUNT < 0) - { - printf("\nPROCESS_COUNT to greater than or equal to 1\n"); - return -1; - } - - WORKER_THREAD_MULTIPLIER_COUNT = atoi(argv[2]); - if( WORKER_THREAD_MULTIPLIER_COUNT < 1 || WORKER_THREAD_MULTIPLIER_COUNT > 64) - { - printf("\nWORKER_THREAD_MULTIPLIER_COUNT to be greater than or equal to 1 or less than or equal to 64\n"); - return -1; - } - - REPEAT_COUNT = atoi(argv[3]); - if( REPEAT_COUNT < 1) - { - printf("\nREPEAT_COUNT to greater than or equal to 1\n"); - return -1; - } - - RELATION_ID = atoi(argv[4]); - if( RELATION_ID < 1) - { - printf("\nRELATION_ID to be greater than or equal to 1\n"); - return -1; - } - - - - return 0; -} - -PALTEST(composite_threading_threadsuspension_paltest_threading_threadsuspension, "composite/threading/threadsuspension/paltest_threading_threadsuspension") -{ - unsigned int i = 0; - HANDLE hProcess[MAXIMUM_WAIT_OBJECTS]; - DWORD processReturnCode = 0; - int testReturnCode = PASS; - STARTUPINFO si[MAXIMUM_WAIT_OBJECTS]; - PROCESS_INFORMATION pi[MAXIMUM_WAIT_OBJECTS]; - - FILE *hFile; - char fileName[MAX_PATH]; - struct applicationStatistics appStats; - - DWORD dwStart=0; - - char lpCommandLine[MAX_PATH] = ""; - - char build[] ="0000.00"; - int returnCode = 0; - - if(0 != (PAL_Initialize(argc, argv))) - { - return ( FAIL ); - } - - //Initialize Application Statistics Structure - appStats.relationId=RELATION_ID; - appStats.operationTime=0; - appStats.buildNumber = getBuildNumber(); - //appStats.buildNumber = build; - appStats.processCount = 0; - appStats.threadCount = 0; - appStats.repeatCount = 0; - - - - - -//Start Process Time Capture -dwStart = GetTickCount(); - - if(GetParameters(argc, argv)) - { - Fail("Error in obtaining the parameters\n"); - } - -//Assign Correct Values to the Application Stats Structure - appStats.relationId=RELATION_ID; - appStats.processCount = USE_PROCESS_COUNT; - appStats.threadCount = THREAD_COUNT ; - appStats.repeatCount = REPEAT_COUNT; - - Trace("Relation ID: %d \n", RELATION_ID); - Trace("USE_PROCESS_COUNT: %d \n", USE_PROCESS_COUNT); - Trace("WORKER_THREAD_MULTIPLIER_COUNT: %d \n", WORKER_THREAD_MULTIPLIER_COUNT); - Trace("REPEAT_COUNT: %d \n", REPEAT_COUNT); - - -_snprintf(fileName, MAX_PATH, "main_threadsuspension_%d_.txt",appStats.relationId); - - hFile = fopen(fileName, "w+"); -if(hFile == NULL) - { - Fail("Error in opening file to write application results for Thread Suspension Test with error code %d \n", GetLastError() ); - } - - - - for( i = 0; i < USE_PROCESS_COUNT; i++ ) - { - - ZeroMemory( lpCommandLine, MAX_PATH ); - if ( _snprintf( lpCommandLine, MAX_PATH-1, "threadsuspension %d %d %d %d", i, WORKER_THREAD_MULTIPLIER_COUNT, REPEAT_COUNT, RELATION_ID) < 0 ) - { - Trace ("Error: Insufficient commandline string length for iteration [%d]\n", i); - } - - /* Zero the data structure space */ - ZeroMemory ( &pi[i], sizeof(pi[i]) ); - ZeroMemory ( &si[i], sizeof(si[i]) ); - - /* Set the process flags and standard io handles */ - si[i].cb = sizeof(si[i]); - - //Printing the Command Line - //Trace("Command Line \t %s \n", lpCommandLine); - - //Create Process - if(!CreateProcess( NULL, /* lpApplicationName*/ - lpCommandLine, /* lpCommandLine */ - NULL, /* lpProcessAttributes */ - NULL, /* lpThreadAttributes */ - TRUE, /* bInheritHandles */ - 0, /* dwCreationFlags, */ - NULL, /* lpEnvironment */ - NULL, /* pCurrentDirectory */ - &si[i], /* lpStartupInfo */ - &pi[i] /* lpProcessInformation */ - )) - { - Fail("Process Not created for [%d] and GetLastError value is %d\n", i, GetLastError()); - - } - else - { - hProcess[i] = pi[i].hProcess; - //Trace("Process created for [%d]\n", i); - } - - } - - returnCode = WaitForMultipleObjects( USE_PROCESS_COUNT, hProcess, TRUE, INFINITE); - if( WAIT_OBJECT_0 != returnCode ) - { - Trace("Wait for Object(s) @ Main thread for %d processes returned %d, and GetLastError value is %d\n", USE_PROCESS_COUNT, returnCode, GetLastError()); - testReturnCode = FAIL; - } - - for( i = 0; i < USE_PROCESS_COUNT; i++ ) - { - /* check the exit code from the process */ - if( ! GetExitCodeProcess( pi[i].hProcess, &processReturnCode ) ) - { - Trace( "GetExitCodeProcess call failed for iteration %d with error code %u\n", - i, GetLastError() ); - - testReturnCode = FAIL; - } - - if(processReturnCode == FAIL) - { - Trace( "Process [%d] failed and returned FAIL\n", i); - testReturnCode = FAIL; - } - - if(!CloseHandle(pi[i].hThread)) - { - Trace("Error:%d: CloseHandle failed for Process [%d] hThread\n", GetLastError(), i); - testReturnCode = FAIL; - } - - if(!CloseHandle(pi[i].hProcess) ) - { - Trace("Error:%d: CloseHandle failed for Process [%d] hProcess\n", GetLastError(), i); - testReturnCode = FAIL; - } - } - -//Get the end time of the process -appStats.operationTime = GetTickCount() - dwStart; - -if( testReturnCode == PASS) - { - Trace("Test Passed\n"); - } - else - { - Trace("Test Failed\n"); - } - -//Write Process Result Contents to File -if(hFile!= NULL) - { - fprintf(hFile, "%lu,%d,%d,%d,%d,%s\n", appStats.operationTime, appStats.relationId, appStats.processCount, appStats.threadCount, appStats.repeatCount, appStats.buildNumber); - } - -if (0!=fclose(hFile)) -{ - Trace("Error:%d: fclose failed for file %s\n", GetLastError(), fileName); -} - PAL_Terminate(); - - -if( testReturnCode == PASS) - { - return PASS; - } - else - { - return FAIL; - - } - -} diff --git a/src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension/readme.txt b/src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension/readme.txt deleted file mode 100644 index d722f1d127655..0000000000000 --- a/src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension/readme.txt +++ /dev/null @@ -1,11 +0,0 @@ -To compile: - -1) create a dat file (say threadsuspension.dat) with contents: -PAL,Composite,palsuite\composite\threading\threadsuspension,wfmo=mainWrapper.c threadsuspension.c,,, - -2) perl rrunmod.pl -r threadsuspension.dat - - -To execute: -mainWrapper [PROCESS_COUNT] [THREAD_COUNT] [REPEAT_COUNT] - diff --git a/src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension/threadsuspension.cpp b/src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension/threadsuspension.cpp deleted file mode 100644 index 9e0492d72cd6c..0000000000000 --- a/src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension/threadsuspension.cpp +++ /dev/null @@ -1,906 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source: \composite\threading\threadsuspension\threadsuspension.c -** -** Purpose: To verify Thread Suspension Reegneering effort for this milestone - - PsedoCode: - - Preparation: - Create PROCESS_COUNT processes. - Test: - Create Worker Thread - Start Reading and writing to a File - - Create Worker Thread - In an infinite loop do the following - Enter Critical Section - Increment Counter - Leave Critical Section - - Create Worker Thread - Allocate Memory and Free Memory - - Create Worker Thread - In a tight loop add numbers - - In a loop repeated REPEAT_COUNT times - - Create Thread - - Suspend all worker threads - Resume all worker threads - - At the end of the loop call PAL_Shutdown - - Parameters: - PROCESS_COUNT: Number of processes - WORKER_THREAD_MULTIPLIER_COUNT: Number of instances of worker threads in each process - REPEAT_COUNT: The number of times to execute the loop. - - Statistics Captured: - Total elapsed time - MTBF - - - Scenario: -** - One thread suspends all remaining threads which are in the middle of doing some work and resume all threads - Thread 1: Reading and Writing File - Thread 2: Enter and Leave Critical Section - Thread 3: Allocating chunks of memory - Thread 4: Perform Unsafe Operation (printf, malloc) - Thread 5: Suspends Thread 1 to Thread 4 and resumes them - -** -** -** -** Dependencies: -** -** - -** -**=========================================================*/ - -#include -#include "resultbuffer.h" - -#define BUFSIZE 4096 -#define NUMBER_OF_WORKER_THREAD_TYPES 4 -#define THREAD_MAX 64 - -#define TEST_FAIL 1 -#define TEST_PASS 0 - - -DWORD GLOBAL_COUNTER ; -DWORD UNIQUE_FILE_NUMBER=0; -HANDLE g_hEvent; - -bool failFlag = false; //To Track failure at the Operation Level - -// 2 dimensional array to hold thread handles for each worker thread -HANDLE hThread[NUMBER_OF_WORKER_THREAD_TYPES][THREAD_MAX]; - -/*unsigned int g_readfileoperation; -unsigned int g_enterleavecsoperation; -unsigned int g_allocatefreeoperation; -unsigned int g_doworintightloop; -*/ - -int TYPES_OF_WORKER_THREAD = NUMBER_OF_WORKER_THREAD_TYPES; - -int testStatus=TEST_PASS; //Indicates test failure - - -struct statistics{ - unsigned int processId; - unsigned int operationsFailed; - unsigned int operationsPassed; - unsigned int operationsTotal; - DWORD operationTime; - unsigned int relationId; -}; - -struct processStatistics{ - unsigned int processId; - DWORD operationTime; - unsigned int relationId; -}; - -/* Results Buffer */ -ResultBuffer *resultBuffer; - - -/* Test Input Variables */ -unsigned int USE_PROCESS_COUNT = 0; //Identifies the Process number. There could potentially -unsigned int WORKER_THREAD_MULTIPLIER_COUNT = 0; //In this test case this represents the number of worker thread instances -unsigned int REPEAT_COUNT = 0; //Number of Suspend Resume operation of worker threads -unsigned int RELATION_ID = 0; - - - -CRITICAL_SECTION CriticalSectionM; /* Critical Section Object (used as mutex) */ -CRITICAL_SECTION g_csUniqueFileName; - -void PALAPI setup(void); -void PALAPI cleanup(void); -void PALAPI incrementCounter(void); -DWORD PALAPI readfile( LPVOID); -DWORD PALAPI enterandleave_cs( LPVOID); -DWORD PALAPI allocateandfree_memory( LPVOID); -DWORD PALAPI doworkintightloop_cs( LPVOID); -DWORD PALAPI suspendandresumethreads( LPVOID); -int GetParameters(int, char * *); - - -//Main Entry for the Thread Suspension Test Case -PALTEST(composite_threading_threadsuspension_paltest_threading_threadsuspension, "composite/threading/threadsuspension/paltest_threading_threadsuspension") -{ - -/* -* Parameter to the threads that will be created -*/ - - -DWORD dwThrdParam = 0; -DWORD dwStart; - -/* Variables to capture the file name and the file pointer*/ -char fileName[MAX_PATH]; -char processFileName[MAX_PATH]; - -FILE *hFile, *hProcessFile; -struct statistics* buffer; -struct processStatistics *processBuffer; - -struct processStatistics processStats; - -struct statistics* tmpBuf = NULL; -int statisticsSize = 0; - -DWORD dwThreadId=0; -HANDLE hMainThread; -unsigned int i = 0; -int j = 0; - - -/* -* PAL Initialize -*/ - -if(0 != (PAL_Initialize(argc, argv))) - { - return FAIL; - } - - -//Get Parameters -if(GetParameters(argc, argv)) - { - Fail("Error in obtaining the parameters\n"); - } - - -//Setup for Process Result Collection -statisticsSize = sizeof(struct statistics); -_snprintf(processFileName, MAX_PATH, "%d_process_threadsuspension_%d_.txt", USE_PROCESS_COUNT, RELATION_ID); -hProcessFile = fopen(processFileName, "w+"); - -if(hProcessFile == NULL) - { - Fail("Error in opening file to write process results for process [%d]\n", USE_PROCESS_COUNT); - } - -//Initialize Process Stats Variables -processStats.operationTime = 0; -processStats.processId = USE_PROCESS_COUNT; -processStats.relationId = RELATION_ID; - -//Start Process Time Capture -dwStart = GetTickCount(); - -//Setup for Thread Result Collection -statisticsSize = sizeof(struct statistics); -_snprintf(fileName, MAX_PATH, "%d_thread_threadsuspension_%d_.txt", USE_PROCESS_COUNT,RELATION_ID); -hFile = fopen(fileName, "w+"); - -if(hFile == NULL) - { - Fail("Error in opening file to write thread results for process [%d]\n", USE_PROCESS_COUNT); - } - -// For each thread we will log relationid (int), processid (int), operations failed (int), passed (int), total (int) -// and number of ticks (DWORD) for the operations -resultBuffer = new ResultBuffer( 1, statisticsSize); - -/* -* Call the Setup Routine -*/ -setup(); - -Trace("WORKER_THREAD_MULTIPLIER_COUNT: %d \n", WORKER_THREAD_MULTIPLIER_COUNT); - -//Create WORKER_THREAD_MULTIPLIER_COUNT Instances of each type of worker thread -for (i=0;igetResultBuffer(i); - fprintf(hFile, "%d,%d,%d,%d,%lu,%d\n", buffer->processId, buffer->operationsFailed, buffer->operationsPassed, buffer->operationsTotal, buffer->operationTime, buffer->relationId ); - } - } - -if (0!=fclose(hFile)) -{ - Fail("Unable to write thread results to file" - "GetLastError returned %d\n", GetLastError()); -} - -cleanup(); - -if (failFlag == TRUE) -{ - return FAIL; -} -else -{ - return PASS; -} -} - - -/* -* Setup for the test case -*/ - -VOID -setup(VOID) -{ - /*Delete All Temporary Files Created by the previous execution of the test case*/ - HANDLE hSearch; - BOOL fFinished = FALSE; - WIN32_FIND_DATA FileData; - - //Start searching for .tmp files in the current directory. - hSearch = FindFirstFile("*.tmp*", &FileData); - if (hSearch == INVALID_HANDLE_VALUE) - { - //No Files That Matched Criteria - fFinished = TRUE; - } - - //Delete all files that match the pattern - while (!fFinished) - { - if (!DeleteFile(FileData.cFileName)) - { - Trace("Setup: Could not delete temporary file %s\n",FileData.cFileName ); - Fail ("GetLastError returned %d\n", GetLastError()); - } - if (!FindNextFile(hSearch, &FileData)) - { - if (GetLastError() == ERROR_NO_MORE_FILES) - { - fFinished = TRUE; - } - else - { - Fail("Unable to Delete Temporary Files, GetLastError is %d \n", GetLastError()); - } - } - } - - // Close the search handle, only if HANDLE is Valid - if (hSearch != INVALID_HANDLE_VALUE) - { - if (!FindClose(hSearch)) - { - Trace("Setup: Could not close search handle \n"); - Fail ("GetLastError returned %d\n", GetLastError()); - } - } - - g_hEvent = CreateEvent(NULL,TRUE,FALSE, NULL); - if(g_hEvent == NULL) - { - Fail("Create Event Failed\n" - "GetLastError returned %d\n", GetLastError()); - } - - InitializeCriticalSection ( &g_csUniqueFileName); -} - -/* -* Cleanup for the test case -*/ - -VOID -cleanup(VOID) -{ - //DeleteCriticalSection(&g_csUniqueFileName); - PAL_Terminate(); -} - - -VOID -incrementCounter(VOID) -{ - - if (INT_MAX == GLOBAL_COUNTER) - { - GLOBAL_COUNTER = 0; - } - - GLOBAL_COUNTER++; -} - -/* - * Worker Thread - * Read File: Read from a file and write to a temporary file and then delete the temp file - */ -DWORD -PALAPI -readfile( LPVOID lpParam ) -{ - - // Declaring Local Variables - HANDLE hFile,hTempfile; - char buffer[BUFSIZE]; - DWORD dwBytesRead, dwBytesWritten, dwBufSize=BUFSIZE; - DWORD dwWaitResult=0; - char filename[MAX_PATH]; - - //Wait for event to signal to start test - dwWaitResult = WaitForSingleObject(g_hEvent,INFINITE); - if (WAIT_OBJECT_0 != dwWaitResult) - { - Fail ("readfile: Wait for Single Object (g_hEvent) failed. Failing test.\n" - "GetLastError returned %d\n", GetLastError()); - } - - - /*Start Operation*/ - - // Open the existing file. - while(TRUE) - { - - hFile = CreateFile("samplefile.dat", // file name - GENERIC_READ, // open for reading - FILE_SHARE_READ, // Share the file for read - NULL, // default security - OPEN_EXISTING, // existing file only - FILE_ATTRIBUTE_NORMAL, // normal file - NULL); // no template - - if (hFile == INVALID_HANDLE_VALUE) - { - Trace("Could not open file \n"); - Fail ( "GetLastError returned %d\n", GetLastError()); - } - - //Generate Unique File Name to Write - //Enter CS - EnterCriticalSection(&g_csUniqueFileName); - - //Increment Number and assign to local variable - UNIQUE_FILE_NUMBER++; - _snprintf(filename, MAX_PATH, "%d_%d_tempfile.tmp", USE_PROCESS_COUNT,UNIQUE_FILE_NUMBER); - //filename = itoa(UNIQUE_FILE_NUMBER); - //Leave CS - LeaveCriticalSection(&g_csUniqueFileName); - - - // Create a temporary file with name generate above - hTempfile = CreateFile(filename, // file name - GENERIC_WRITE, // open for read/write - 0, // do not share - NULL, // default security - CREATE_ALWAYS, // overwrite existing file - FILE_ATTRIBUTE_NORMAL, // normal file - NULL); // no template - - - if (hTempfile == INVALID_HANDLE_VALUE) - { - Trace("Could not create temporary file\n"); - Fail ( "GetLastError returned %d\n", GetLastError()); - } - - // Read 4K blocks to the buffer. - // Change all characters in the buffer to upper case. - // Write the buffer to the temporary file. - - do - { - if (ReadFile(hFile, buffer, 4096, - &dwBytesRead, NULL)) - { - - WriteFile(hTempfile, buffer, dwBytesRead, - &dwBytesWritten, NULL); - } - } while (dwBytesRead == BUFSIZE); - - - - // Close both files. - if (0==CloseHandle(hFile)) - { - Trace("Could not handle hFile\n"); - Fail ( "GetLastError returned %d\n", GetLastError()); - } - - if (0==CloseHandle(hTempfile)) - { - Trace("Could not handle hTempFile\n"); - Fail ( "GetLastError returned %d\n", GetLastError()); - } - - //Delete the file that was created - if (!DeleteFile(filename)) - { - Trace("Could not delete temporary file %s\n", filename); - Fail ( "GetLastError returned %d\n", GetLastError()); - - } - - //g_readfileoperation++; - } - -/*End Operation*/ - - return 0; -} - - -/* Worker Thread - * Enter and Leave Nested Critical Sections - */ -DWORD -PALAPI -enterandleave_cs( LPVOID lpParam ) -{ - - //Declare Local Variables - - CRITICAL_SECTION lcs; - CRITICAL_SECTION lcsNested; - - DWORD dwWaitResult; - - //Initialize Critical Section Structures - InitializeCriticalSection ( &lcs); - InitializeCriticalSection ( &lcsNested); - - - //Wait for event to signal to start test - dwWaitResult = WaitForSingleObject(g_hEvent,INFINITE); - if (WAIT_OBJECT_0 != dwWaitResult) - { - Fail ("enterandleave_cs: Wait for Single Object (g_hEvent) failed. Failing test.\n" - "GetLastError returned %d\n", GetLastError()); - } - - //Trace("Critical Section Started\n"); - - while(TRUE) - { - EnterCriticalSection(&lcs); - - EnterCriticalSection(&lcsNested); - - incrementCounter(); - - LeaveCriticalSection(&lcsNested); - - LeaveCriticalSection(&lcs); - //g_enterleavecsoperation++; - } - - //Delete Critical Section Structures - - DeleteCriticalSection(&lcs); - DeleteCriticalSection(&lcsNested); - - - return 0; -} - - -/* - * Allocate and Free Memory - */ -DWORD -PALAPI -allocateandfree_memory( LPVOID lpParam ) -{ - - - int i; - char *textArrPtr[64]; - DWORD dwWaitResult; - - //Wait for event to signal to start test - dwWaitResult = WaitForSingleObject(g_hEvent,INFINITE); - if (WAIT_OBJECT_0 != dwWaitResult) - { - Fail ("allocateandfree_memory: Wait for Single Object (g_hEvent) failed. Failing test.\n" - "GetLastError returned %d\n", GetLastError()); - } - - - while(TRUE) - { - - //do allocate and free operation - - for (i=0;i<64;i++) - { - textArrPtr[i] = (char*) malloc(BUFSIZE); - if (textArrPtr[i] == NULL) - { - Fail("Insufficient Memory Available, GetLastError is %d \n", GetLastError()); - testStatus = TEST_FAIL; - } - } - - for (i=0;i<64;i++) - { - free(textArrPtr[i]); - } - //g_allocatefreeoperation++; - } - - - - - return 0; -} - -/* - * Do work in a tight loop - */ -DWORD -PALAPI -doworkintightloop_cs( LPVOID lpParam ) -{ - - unsigned int i; - DWORD dwWaitResult; - - //Wait for event to signal to start test - dwWaitResult = WaitForSingleObject(g_hEvent,INFINITE); - if (WAIT_OBJECT_0 != dwWaitResult) - { - Fail ("doworkintightloop_cs: Wait for Single Object (g_hEvent) failed. Failing test.\n" - "GetLastError returned %d\n", GetLastError()); - } - - i= 0; - while (TRUE) - { - - if (INT_MAX == i) - i =0; - i++; - //g_doworintightloop++; - } - - return 0; -} - - -/* - * Main Test Case worker thread which will suspend and resume all other worker threads - */ -DWORD -PALAPI -suspendandresumethreads( LPVOID lpParam ) -{ - - unsigned int loopcount = REPEAT_COUNT; - int Id=(int)lpParam; - unsigned int i,j,k; - DWORD dwStart; - DWORD dwWaitResult=0; - DWORD dwLastError = 0; - struct statistics stats; - struct statistics* buffer; - - - - //Initialize the Statistics Structure - stats.relationId = RELATION_ID; - stats.processId = USE_PROCESS_COUNT; - stats.operationsFailed = 0; - stats.operationsPassed = 0; - stats.operationsTotal = 0; - stats.operationTime = 0; - - - - //Wait for event to signal to start test - WaitForSingleObject(g_hEvent,INFINITE); - if (WAIT_OBJECT_0 != dwWaitResult) - { - Fail ("suspendandresumethreads: Wait for Single Object (g_hEvent) failed. Failing test.\n" - "GetLastError returned %d\n", GetLastError()); - } - - - //Capture Start Import - dwStart = GetTickCount(); - - for(i = 0; i < loopcount; i++) - { - - failFlag = false; - - //Suspend Worker Threads - for (k=0;kLogResult(Id, (char *)&stats)) - { - Fail("Error while writing to shared memory, Thread Id is[%d] and Process id is [%d]\n", Id, USE_PROCESS_COUNT); - } - - buffer = (struct statistics *)resultBuffer->getResultBuffer(Id); - //Trace("\n%d,%d,%d,%lu\n", buffer->operationsFailed, buffer->operationsPassed, buffer->operationsTotal, buffer->operationTime ); - - - return 0; -} - - - -int GetParameters( int argc, char **argv) -{ - - if( (argc != 5) || ((argc == 1) && !strcmp(argv[1],"/?")) - || !strcmp(argv[1],"/h") || !strcmp(argv[1],"/H")) - { - Trace("PAL -Composite Thread Suspension Test\n"); - Trace("Usage:\n"); - Trace("\t[PROCESS_COUNT] Greater than or Equal to 1 \n"); - Trace("\t[WORKER_THREAD_MULTIPLIER_COUNT] Greater than or Equal to 1 and Less than or Equal to 64 \n"); - Trace("\t[REPEAT_COUNT] Greater than or Equal to 1\n"); - Trace("\t[RELATION_ID [greater than or Equal to 1]\n"); - return -1; - } - -// Trace("Args 1 is [%s], Arg 2 is [%s], Arg 3 is [%s]\n", argv[1], argv[2], argv[3]); - - USE_PROCESS_COUNT = atoi(argv[1]); - if( USE_PROCESS_COUNT < 0) - { - Trace("\nPROCESS_COUNT to greater than or equal to 1\n"); - return -1; - } - - WORKER_THREAD_MULTIPLIER_COUNT = atoi(argv[2]); - if( WORKER_THREAD_MULTIPLIER_COUNT < 1 || WORKER_THREAD_MULTIPLIER_COUNT > 64) - { - Trace("\nWORKER_THREAD_MULTIPLIER_COUNT to be greater than or equal to 1 or less than or equal to 64\n"); - return -1; - } - - REPEAT_COUNT = atoi(argv[3]); - if( REPEAT_COUNT < 1) - { - Trace("\nREPEAT_COUNT to greater than or equal to 1\n"); - return -1; - } - - RELATION_ID = atoi(argv[4]); - if( RELATION_ID < 1) - { - Trace("\nRELATION_ID to be greater than or equal to 1\n"); - return -1; - } - return 0; -} diff --git a/src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension_switchthread/mainWrapper.cpp b/src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension_switchthread/mainWrapper.cpp deleted file mode 100644 index f934bd4218b54..0000000000000 --- a/src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension_switchthread/mainWrapper.cpp +++ /dev/null @@ -1,273 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/* -Source Code: mainWrapper.c - -mainWrapper.c creates Composite Test Case Processes and waits for all processes to get over - -Algorithm -o Create PROCESS_COUNT processes. - -Author: RameshG -*/ - -#include -#include "resulttime.h" - -/* Test Input Variables */ -unsigned int USE_PROCESS_COUNT = 0; //default -unsigned int WORKER_THREAD_MULTIPLIER_COUNT = 0; //default -unsigned int REPEAT_COUNT = 0; //default -unsigned int SLEEP_LENGTH = 0; //default -unsigned int RELATION_ID = 0;//default -unsigned int THREAD_COUNT = 1; //There is only one suspender and resume thread for this test case - -char *testCaseName; - - -struct applicationStatistics{ - DWORD operationTime; - unsigned int relationId; - unsigned int processCount; - unsigned int threadCount; - unsigned int repeatCount; - char* buildNumber; - -}; - - -int GetParameters( int argc, char **argv) -{ - - if( (argc != 5) || ((argc == 1) && !strcmp(argv[1],"/?")) - || !strcmp(argv[1],"/h") || !strcmp(argv[1],"/H")) - { - printf("PAL -Composite Thread Suspension Test\n"); - printf("Usage:\n"); - printf("\t[PROCESS_COUNT] Greater than or Equal to 1 \n"); - printf("\t[WORKER_THREAD_MULTIPLIER_COUNT] Greater than or Equal to 1 and Less than or Equal to %d \n", MAXIMUM_WAIT_OBJECTS); - printf("\t[REPEAT_COUNT] Greater than or Equal to 1\n"); - printf("\t[RELATION_ID [greater than or Equal to 1]\n"); - return -1; - } - - // Trace("Args 1 is [%s], Arg 2 is [%s], Arg 3 is [%s]\n", argv[1], argv[2], argv[3]); - - USE_PROCESS_COUNT = atoi(argv[1]); - if( USE_PROCESS_COUNT < 0) - { - printf("\nPROCESS_COUNT to greater than or equal to 1\n"); - return -1; - } - - WORKER_THREAD_MULTIPLIER_COUNT = atoi(argv[2]); - if( WORKER_THREAD_MULTIPLIER_COUNT < 1 || WORKER_THREAD_MULTIPLIER_COUNT > 64) - { - printf("\nWORKER_THREAD_MULTIPLIER_COUNT to be greater than or equal to 1 or less than or equal to 64\n"); - return -1; - } - - REPEAT_COUNT = atoi(argv[3]); - if( REPEAT_COUNT < 1) - { - printf("\nREPEAT_COUNT to greater than or equal to 1\n"); - return -1; - } - - RELATION_ID = atoi(argv[4]); - if( RELATION_ID < 1) - { - printf("\nRELATION_ID to be greater than or equal to 1\n"); - return -1; - } - - - - return 0; -} - -PALTEST(composite_threading_threadsuspension_switchthread_paltest_threading_threadsuspension_switchthread, "composite/threading/threadsuspension_switchthread/paltest_threading_threadsuspension_switchthread") -{ - unsigned int i = 0; - HANDLE hProcess[MAXIMUM_WAIT_OBJECTS]; - DWORD processReturnCode = 0; - int testReturnCode = PASS; - STARTUPINFO si[MAXIMUM_WAIT_OBJECTS]; - PROCESS_INFORMATION pi[MAXIMUM_WAIT_OBJECTS]; - - FILE *hFile; - char fileName[MAX_PATH]; - struct applicationStatistics appStats; - - DWORD dwStart=0; - - char lpCommandLine[MAX_PATH] = ""; - - char build[] ="0000.00"; - int returnCode = 0; - - if(0 != (PAL_Initialize(argc, argv))) - { - return ( FAIL ); - } - - //Initialize Application Statistics Structure - appStats.relationId=RELATION_ID; - appStats.operationTime=0; - appStats.buildNumber = getBuildNumber(); - //appStats.buildNumber = build; - appStats.processCount = 0; - appStats.threadCount = 0; - appStats.repeatCount = 0; - - - - - -//Start Process Time Capture -dwStart = GetTickCount(); - - if(GetParameters(argc, argv)) - { - Fail("Error in obtaining the parameters\n"); - } - -//Assign Correct Values to the Application Stats Structure - appStats.relationId=RELATION_ID; - appStats.processCount = USE_PROCESS_COUNT; - appStats.threadCount = THREAD_COUNT ; - appStats.repeatCount = REPEAT_COUNT; - - Trace("Relation ID: %d \n", RELATION_ID); - Trace("USE_PROCESS_COUNT: %d \n", USE_PROCESS_COUNT); - Trace("WORKER_THREAD_MULTIPLIER_COUNT: %d \n", WORKER_THREAD_MULTIPLIER_COUNT); - Trace("REPEAT_COUNT: %d \n", REPEAT_COUNT); - - -_snprintf(fileName, MAX_PATH, "main_threadsuspension_%d_.txt",appStats.relationId); - - hFile = fopen(fileName, "w+"); -if(hFile == NULL) - { - Fail("Error in opening file to write application results for Thread Suspension Test with error code %d \n", GetLastError() ); - } - - - - for( i = 0; i < USE_PROCESS_COUNT; i++ ) - { - - ZeroMemory( lpCommandLine, MAX_PATH ); - if ( _snprintf( lpCommandLine, MAX_PATH-1, "threadsuspension %d %d %d %d", i, WORKER_THREAD_MULTIPLIER_COUNT, REPEAT_COUNT, RELATION_ID) < 0 ) - { - Trace ("Error: Insufficient commandline string length for iteration [%d]\n", i); - } - - /* Zero the data structure space */ - ZeroMemory ( &pi[i], sizeof(pi[i]) ); - ZeroMemory ( &si[i], sizeof(si[i]) ); - - /* Set the process flags and standard io handles */ - si[i].cb = sizeof(si[i]); - - //Printing the Command Line - //Trace("Command Line \t %s \n", lpCommandLine); - - //Create Process - if(!CreateProcess( NULL, /* lpApplicationName*/ - lpCommandLine, /* lpCommandLine */ - NULL, /* lpProcessAttributes */ - NULL, /* lpThreadAttributes */ - TRUE, /* bInheritHandles */ - 0, /* dwCreationFlags, */ - NULL, /* lpEnvironment */ - NULL, /* pCurrentDirectory */ - &si[i], /* lpStartupInfo */ - &pi[i] /* lpProcessInformation */ - )) - { - Fail("Process Not created for [%d] and GetLastError value is %d\n", i, GetLastError()); - - } - else - { - hProcess[i] = pi[i].hProcess; - //Trace("Process created for [%d]\n", i); - } - - } - - returnCode = WaitForMultipleObjects( USE_PROCESS_COUNT, hProcess, TRUE, INFINITE); - if( WAIT_OBJECT_0 != returnCode ) - { - Trace("Wait for Object(s) @ Main thread for %d processes returned %d, and GetLastError value is %d\n", USE_PROCESS_COUNT, returnCode, GetLastError()); - testReturnCode = FAIL; - } - - for( i = 0; i < USE_PROCESS_COUNT; i++ ) - { - /* check the exit code from the process */ - if( ! GetExitCodeProcess( pi[i].hProcess, &processReturnCode ) ) - { - Trace( "GetExitCodeProcess call failed for iteration %d with error code %u\n", - i, GetLastError() ); - - testReturnCode = FAIL; - } - - if(processReturnCode == FAIL) - { - Trace( "Process [%d] failed and returned FAIL\n", i); - testReturnCode = FAIL; - } - - if(!CloseHandle(pi[i].hThread)) - { - Trace("Error:%d: CloseHandle failed for Process [%d] hThread\n", GetLastError(), i); - testReturnCode = FAIL; - } - - if(!CloseHandle(pi[i].hProcess) ) - { - Trace("Error:%d: CloseHandle failed for Process [%d] hProcess\n", GetLastError(), i); - testReturnCode = FAIL; - } - } - -//Get the end time of the process -appStats.operationTime = GetTickCount() - dwStart; - -if( testReturnCode == PASS) - { - Trace("Test Passed\n"); - } - else - { - Trace("Test Failed\n"); - } - -//Write Process Result Contents to File -if(hFile!= NULL) - { - fprintf(hFile, "%lu,%d,%d,%d,%d,%s\n", appStats.operationTime, appStats.relationId, appStats.processCount, appStats.threadCount, appStats.repeatCount, appStats.buildNumber); - } - -if (0!=fclose(hFile)) -{ - Trace("Error:%d: fclose failed for file %s\n", GetLastError(), fileName); -} - PAL_Terminate(); - - -if( testReturnCode == PASS) - { - return PASS; - } - else - { - return FAIL; - - } - -} diff --git a/src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension_switchthread/readme.txt b/src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension_switchthread/readme.txt deleted file mode 100644 index d722f1d127655..0000000000000 --- a/src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension_switchthread/readme.txt +++ /dev/null @@ -1,11 +0,0 @@ -To compile: - -1) create a dat file (say threadsuspension.dat) with contents: -PAL,Composite,palsuite\composite\threading\threadsuspension,wfmo=mainWrapper.c threadsuspension.c,,, - -2) perl rrunmod.pl -r threadsuspension.dat - - -To execute: -mainWrapper [PROCESS_COUNT] [THREAD_COUNT] [REPEAT_COUNT] - diff --git a/src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension_switchthread/threadsuspension.cpp b/src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension_switchthread/threadsuspension.cpp deleted file mode 100644 index d1f11d304f5a9..0000000000000 --- a/src/coreclr/pal/tests/palsuite/composite/threading/threadsuspension_switchthread/threadsuspension.cpp +++ /dev/null @@ -1,913 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*============================================================ -** -** Source: \composite\threading\threadsuspension\threadsuspension.c -** -** Purpose: To verify Thread Suspension Reegneering effort for this milestone - - PsedoCode: - - Preparation: - Create PROCESS_COUNT processes. - Test: - Create Worker Thread - Start Reading and writing to a File - - Create Worker Thread - In an infinite loop do the following - Enter Critical Section - Increment Counter - Leave Critical Section - - Create Worker Thread - Allocate Memory and Free Memory - - Create Worker Thread - In a tight loop add numbers - - In a loop repeated REPEAT_COUNT times - - Create Thread - - Suspend all worker threads - Resume all worker threads - - At the end of the loop call PAL_Shutdown - - Parameters: - PROCESS_COUNT: Number of processes - WORKER_THREAD_MULTIPLIER_COUNT: Number of instances of worker threads in each process - REPEAT_COUNT: The number of times to execute the loop. - - Statistics Captured: - Total elapsed time - MTBF - - - Scenario: -** - One thread suspends all remaining threads which are in the middle of doing some work and resume all threads - Thread 1: Reading and Writing File - Thread 2: Enter and Leave Critical Section - Thread 3: Allocating chunks of memory - Thread 4: Perform Unsafe Operation (printf, malloc) - Thread 5: Suspends Thread 1 to Thread 4 and resumes them - -** -** -** -** Dependencies: -** -** - -** -**=========================================================*/ - -#include -#include "resultbuffer.h" - -#define BUFSIZE 4096 -#define NUMBER_OF_WORKER_THREAD_TYPES 4 -#define THREAD_MAX 64 - -#define TEST_FAIL 1 -#define TEST_PASS 0 - - -DWORD GLOBAL_COUNTER ; -DWORD UNIQUE_FILE_NUMBER=0; -HANDLE g_hEvent; - -bool failFlag = false; //To Track failure at the Operation Level - -// 2 dimensional array to hold thread handles for each worker thread -HANDLE hThread[NUMBER_OF_WORKER_THREAD_TYPES][THREAD_MAX]; - -/*unsigned int g_readfileoperation; -unsigned int g_enterleavecsoperation; -unsigned int g_allocatefreeoperation; -unsigned int g_doworintightloop; -*/ - -int TYPES_OF_WORKER_THREAD = NUMBER_OF_WORKER_THREAD_TYPES; - -int testStatus=TEST_PASS; //Indicates test failure - - -struct statistics{ - unsigned int processId; - unsigned int operationsFailed; - unsigned int operationsPassed; - unsigned int operationsTotal; - DWORD operationTime; - unsigned int relationId; -}; - -struct processStatistics{ - unsigned int processId; - DWORD operationTime; - unsigned int relationId; -}; - -/* Results Buffer */ -ResultBuffer *resultBuffer; - - -/* Test Input Variables */ -unsigned int USE_PROCESS_COUNT = 0; //Identifies the Process number. There could potentially -unsigned int WORKER_THREAD_MULTIPLIER_COUNT = 0; //In this test case this represents the number of worker thread instances -unsigned int REPEAT_COUNT = 0; //Number of Suspend Resume operation of worker threads -unsigned int RELATION_ID = 0; - - - -CRITICAL_SECTION CriticalSectionM; /* Critical Section Object (used as mutex) */ -CRITICAL_SECTION g_csUniqueFileName; - -void PALAPI setup(void); -void PALAPI cleanup(void); -void PALAPI incrementCounter(void); -DWORD PALAPI readfile( LPVOID); -DWORD PALAPI enterandleave_cs( LPVOID); -DWORD PALAPI allocateandfree_memory( LPVOID); -DWORD PALAPI doworkintightloop_cs( LPVOID); -DWORD PALAPI suspendandresumethreads( LPVOID); -int GetParameters(int, char * *); - - -//Main Entry for the Thread Suspension Test Case -PALTEST(composite_threading_threadsuspension_switchthread_paltest_threading_threadsuspension_switchthread, "composite/threading/threadsuspension_switchthread/paltest_threading_threadsuspension_switchthread") -{ - -/* -* Parameter to the threads that will be created -*/ - - -DWORD dwThrdParam = 0; -DWORD dwStart; - -/* Variables to capture the file name and the file pointer*/ -char fileName[MAX_PATH]; -char processFileName[MAX_PATH]; - -FILE *hFile, *hProcessFile; -struct statistics* buffer; -struct processStatistics *processBuffer; - -struct processStatistics processStats; - -struct statistics* tmpBuf = NULL; -int statisticsSize = 0; - -DWORD dwThreadId=0; -HANDLE hMainThread; -unsigned int i = 0; -int j = 0; - - -/* -* PAL Initialize -*/ - -if(0 != (PAL_Initialize(argc, argv))) - { - return FAIL; - } - - -//Get Parameters -if(GetParameters(argc, argv)) - { - Fail("Error in obtaining the parameters\n"); - } - - -//Setup for Process Result Collection -statisticsSize = sizeof(struct statistics); -_snprintf(processFileName, MAX_PATH, "%d_process_threadsuspension_%d_.txt", USE_PROCESS_COUNT, RELATION_ID); -hProcessFile = fopen(processFileName, "w+"); - -if(hProcessFile == NULL) - { - Fail("Error in opening file to write process results for process [%d]\n", USE_PROCESS_COUNT); - } - -//Initialize Process Stats Variables -processStats.operationTime = 0; -processStats.processId = USE_PROCESS_COUNT; -processStats.relationId = RELATION_ID; - -//Start Process Time Capture -dwStart = GetTickCount(); - -//Setup for Thread Result Collection -statisticsSize = sizeof(struct statistics); -_snprintf(fileName, MAX_PATH, "%d_thread_threadsuspension_%d_.txt", USE_PROCESS_COUNT,RELATION_ID); -hFile = fopen(fileName, "w+"); - -if(hFile == NULL) - { - Fail("Error in opening file to write thread results for process [%d]\n", USE_PROCESS_COUNT); - } - -// For each thread we will log relationid (int), processid (int), operations failed (int), passed (int), total (int) -// and number of ticks (DWORD) for the operations -resultBuffer = new ResultBuffer( 1, statisticsSize); - -/* -* Call the Setup Routine -*/ -setup(); - -Trace("WORKER_THREAD_MULTIPLIER_COUNT: %d \n", WORKER_THREAD_MULTIPLIER_COUNT); - -//Create WORKER_THREAD_MULTIPLIER_COUNT Instances of each type of worker thread -for (i=0;igetResultBuffer(i); - fprintf(hFile, "%d,%d,%d,%d,%lu,%d\n", buffer->processId, buffer->operationsFailed, buffer->operationsPassed, buffer->operationsTotal, buffer->operationTime, buffer->relationId ); - } - } - -if (0!=fclose(hFile)) -{ - Fail("Unable to write thread results to file" - "GetLastError returned %d\n", GetLastError()); -} - -cleanup(); - -if (failFlag == TRUE) -{ - return FAIL; -} -else -{ - return PASS; -} -} - - -/* -* Setup for the test case -*/ - -VOID -setup(VOID) -{ - /*Delete All Temporary Files Created by the previous execution of the test case*/ - HANDLE hSearch; - BOOL fFinished = FALSE; - WIN32_FIND_DATA FileData; - - //Start searching for .tmp files in the current directory. - hSearch = FindFirstFile("*.tmp*", &FileData); - if (hSearch == INVALID_HANDLE_VALUE) - { - //No Files That Matched Criteria - fFinished = TRUE; - } - - //Delete all files that match the pattern - while (!fFinished) - { - if (!DeleteFile(FileData.cFileName)) - { - Trace("Setup: Could not delete temporary file %s\n",FileData.cFileName ); - Fail ("GetLastError returned %d\n", GetLastError()); - } - if (!FindNextFile(hSearch, &FileData)) - { - if (GetLastError() == ERROR_NO_MORE_FILES) - { - fFinished = TRUE; - } - else - { - Fail("Unable to Delete Temporary Files, GetLastError is %d \n", GetLastError()); - } - } - } - - // Close the search handle, only if HANDLE is Valid - if (hSearch != INVALID_HANDLE_VALUE) - { - if (!FindClose(hSearch)) - { - Trace("Setup: Could not close search handle \n"); - Fail ("GetLastError returned %d\n", GetLastError()); - } - } - - g_hEvent = CreateEvent(NULL,TRUE,FALSE, NULL); - if(g_hEvent == NULL) - { - Fail("Create Event Failed\n" - "GetLastError returned %d\n", GetLastError()); - } - - InitializeCriticalSection ( &g_csUniqueFileName); -} - -/* -* Cleanup for the test case -*/ - -VOID -cleanup(VOID) -{ - //DeleteCriticalSection(&g_csUniqueFileName); - PAL_Terminate(); -} - - -VOID -incrementCounter(VOID) -{ - - if (INT_MAX == GLOBAL_COUNTER) - { - GLOBAL_COUNTER = 0; - } - - GLOBAL_COUNTER++; -} - -/* - * Worker Thread - * Read File: Read from a file and write to a temporary file and then delete the temp file - */ -DWORD -PALAPI -readfile( LPVOID lpParam ) -{ - - // Declaring Local Variables - HANDLE hFile,hTempfile; - char buffer[BUFSIZE]; - DWORD dwBytesRead, dwBytesWritten, dwBufSize=BUFSIZE; - DWORD dwWaitResult=0; - char filename[MAX_PATH]; - - //Wait for event to signal to start test - dwWaitResult = WaitForSingleObject(g_hEvent,INFINITE); - if (WAIT_OBJECT_0 != dwWaitResult) - { - Fail ("readfile: Wait for Single Object (g_hEvent) failed. Failing test.\n" - "GetLastError returned %d\n", GetLastError()); - } - - - /*Start Operation*/ - - // Open the existing file. - while(TRUE) - { - - hFile = CreateFile("samplefile.dat", // file name - GENERIC_READ, // open for reading - FILE_SHARE_READ, // Share the file for read - NULL, // default security - OPEN_EXISTING, // existing file only - FILE_ATTRIBUTE_NORMAL, // normal file - NULL); // no template - - if (hFile == INVALID_HANDLE_VALUE) - { - Trace("Could not open file \n"); - Fail ( "GetLastError returned %d\n", GetLastError()); - } - - //Generate Unique File Name to Write - //Enter CS - EnterCriticalSection(&g_csUniqueFileName); - - //Increment Number and assign to local variable - UNIQUE_FILE_NUMBER++; - _snprintf(filename, MAX_PATH, "%d_%d_tempfile.tmp", USE_PROCESS_COUNT,UNIQUE_FILE_NUMBER); - //filename = itoa(UNIQUE_FILE_NUMBER); - //Leave CS - LeaveCriticalSection(&g_csUniqueFileName); - - - // Create a temporary file with name generate above - hTempfile = CreateFile(filename, // file name - GENERIC_WRITE, // open for read/write - 0, // do not share - NULL, // default security - CREATE_ALWAYS, // overwrite existing file - FILE_ATTRIBUTE_NORMAL, // normal file - NULL); // no template - - - if (hTempfile == INVALID_HANDLE_VALUE) - { - Trace("Could not create temporary file\n"); - Fail ( "GetLastError returned %d\n", GetLastError()); - } - - // Read 4K blocks to the buffer. - // Change all characters in the buffer to upper case. - // Write the buffer to the temporary file. - - do - { - if (ReadFile(hFile, buffer, 4096, - &dwBytesRead, NULL)) - { - - WriteFile(hTempfile, buffer, dwBytesRead, - &dwBytesWritten, NULL); - } - } while (dwBytesRead == BUFSIZE); - - - - // Close both files. - if (0==CloseHandle(hFile)) - { - Trace("Could not handle hFile\n"); - Fail ( "GetLastError returned %d\n", GetLastError()); - } - - if (0==CloseHandle(hTempfile)) - { - Trace("Could not handle hTempFile\n"); - Fail ( "GetLastError returned %d\n", GetLastError()); - } - - //Delete the file that was created - if (!DeleteFile(filename)) - { - Trace("Could not delete temporary file %s\n", filename); - Fail ( "GetLastError returned %d\n", GetLastError()); - - } - - SwitchToThread(); - //g_readfileoperation++; - } - -/*End Operation*/ - - return 0; -} - - -/* Worker Thread - * Enter and Leave Nested Critical Sections - */ -DWORD -PALAPI -enterandleave_cs( LPVOID lpParam ) -{ - - //Declare Local Variables - - CRITICAL_SECTION lcs; - CRITICAL_SECTION lcsNested; - - DWORD dwWaitResult; - - //Initialize Critical Section Structures - InitializeCriticalSection ( &lcs); - InitializeCriticalSection ( &lcsNested); - - - //Wait for event to signal to start test - dwWaitResult = WaitForSingleObject(g_hEvent,INFINITE); - if (WAIT_OBJECT_0 != dwWaitResult) - { - Fail ("enterandleave_cs: Wait for Single Object (g_hEvent) failed. Failing test.\n" - "GetLastError returned %d\n", GetLastError()); - } - - //Trace("Critical Section Started\n"); - - while(TRUE) - { - EnterCriticalSection(&lcs); - - EnterCriticalSection(&lcsNested); - - incrementCounter(); - - LeaveCriticalSection(&lcsNested); - - LeaveCriticalSection(&lcs); - - SwitchToThread(); - //g_enterleavecsoperation++; - } - - //Delete Critical Section Structures - - DeleteCriticalSection(&lcs); - DeleteCriticalSection(&lcsNested); - - - return 0; -} - - -/* - * Allocate and Free Memory - */ -DWORD -PALAPI -allocateandfree_memory( LPVOID lpParam ) -{ - - - int i; - char *textArrPtr[64]; - DWORD dwWaitResult; - - //Wait for event to signal to start test - dwWaitResult = WaitForSingleObject(g_hEvent,INFINITE); - if (WAIT_OBJECT_0 != dwWaitResult) - { - Fail ("allocateandfree_memory: Wait for Single Object (g_hEvent) failed. Failing test.\n" - "GetLastError returned %d\n", GetLastError()); - } - - - while(TRUE) - { - - //do allocate and free operation - - for (i=0;i<64;i++) - { - textArrPtr[i] = (char*) malloc(BUFSIZE); - if (textArrPtr[i] == NULL) - { - Fail("Insufficient Memory Available, GetLastError is %d \n", GetLastError()); - testStatus = TEST_FAIL; - } - } - - for (i=0;i<64;i++) - { - free(textArrPtr[i]); - } - - SwitchToThread(); - //g_allocatefreeoperation++; - } - - - - - return 0; -} - -/* - * Do work in a tight loop - */ -DWORD -PALAPI -doworkintightloop_cs( LPVOID lpParam ) -{ - - unsigned int i; - DWORD dwWaitResult; - - //Wait for event to signal to start test - dwWaitResult = WaitForSingleObject(g_hEvent,INFINITE); - if (WAIT_OBJECT_0 != dwWaitResult) - { - Fail ("doworkintightloop_cs: Wait for Single Object (g_hEvent) failed. Failing test.\n" - "GetLastError returned %d\n", GetLastError()); - } - - i= 0; - while (TRUE) - { - - if (INT_MAX == i) - i =0; - i++; - //g_doworintightloop++; - - SwitchToThread(); - } - - return 0; -} - - -/* - * Main Test Case worker thread which will suspend and resume all other worker threads - */ -DWORD -PALAPI -suspendandresumethreads( LPVOID lpParam ) -{ - - unsigned int loopcount = REPEAT_COUNT; - int Id=(int)lpParam; - unsigned int i,j,k; - DWORD dwStart; - DWORD dwWaitResult=0; - DWORD dwLastError = 0; - struct statistics stats; - struct statistics* buffer; - - - - //Initialize the Statistics Structure - stats.relationId = RELATION_ID; - stats.processId = USE_PROCESS_COUNT; - stats.operationsFailed = 0; - stats.operationsPassed = 0; - stats.operationsTotal = 0; - stats.operationTime = 0; - - - - //Wait for event to signal to start test - WaitForSingleObject(g_hEvent,INFINITE); - if (WAIT_OBJECT_0 != dwWaitResult) - { - Fail ("suspendandresumethreads: Wait for Single Object (g_hEvent) failed. Failing test.\n" - "GetLastError returned %d\n", GetLastError()); - } - - - //Capture Start Import - dwStart = GetTickCount(); - - for(i = 0; i < loopcount; i++) - { - - failFlag = false; - - //Suspend Worker Threads - for (k=0;kLogResult(Id, (char *)&stats)) - { - Fail("Error while writing to shared memory, Thread Id is[%d] and Process id is [%d]\n", Id, USE_PROCESS_COUNT); - } - - buffer = (struct statistics *)resultBuffer->getResultBuffer(Id); - //Trace("\n%d,%d,%d,%lu\n", buffer->operationsFailed, buffer->operationsPassed, buffer->operationsTotal, buffer->operationTime ); - - - return 0; -} - - - -int GetParameters( int argc, char **argv) -{ - - if( (argc != 5) || ((argc == 1) && !strcmp(argv[1],"/?")) - || !strcmp(argv[1],"/h") || !strcmp(argv[1],"/H")) - { - Trace("PAL -Composite Thread Suspension Test\n"); - Trace("Usage:\n"); - Trace("\t[PROCESS_COUNT] Greater than or Equal to 1 \n"); - Trace("\t[WORKER_THREAD_MULTIPLIER_COUNT] Greater than or Equal to 1 and Less than or Equal to 64 \n"); - Trace("\t[REPEAT_COUNT] Greater than or Equal to 1\n"); - Trace("\t[RELATION_ID [greater than or Equal to 1]\n"); - return -1; - } - -// Trace("Args 1 is [%s], Arg 2 is [%s], Arg 3 is [%s]\n", argv[1], argv[2], argv[3]); - - USE_PROCESS_COUNT = atoi(argv[1]); - if( USE_PROCESS_COUNT < 0) - { - Trace("\nPROCESS_COUNT to greater than or equal to 1\n"); - return -1; - } - - WORKER_THREAD_MULTIPLIER_COUNT = atoi(argv[2]); - if( WORKER_THREAD_MULTIPLIER_COUNT < 1 || WORKER_THREAD_MULTIPLIER_COUNT > 64) - { - Trace("\nWORKER_THREAD_MULTIPLIER_COUNT to be greater than or equal to 1 or less than or equal to 64\n"); - return -1; - } - - REPEAT_COUNT = atoi(argv[3]); - if( REPEAT_COUNT < 1) - { - Trace("\nREPEAT_COUNT to greater than or equal to 1\n"); - return -1; - } - - RELATION_ID = atoi(argv[4]); - if( RELATION_ID < 1) - { - Trace("\nRELATION_ID to be greater than or equal to 1\n"); - return -1; - } - return 0; -} diff --git a/src/coreclr/pal/tests/palsuite/file_io/FindClose/test1/FindClose.cpp b/src/coreclr/pal/tests/palsuite/file_io/FindClose/test1/FindClose.cpp deleted file mode 100644 index 7226b1d7a0642..0000000000000 --- a/src/coreclr/pal/tests/palsuite/file_io/FindClose/test1/FindClose.cpp +++ /dev/null @@ -1,274 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: FindClose.c -** -** Purpose: Tests the PAL implementation of the FindClose function. -** -** -**===================================================================*/ - - -#include - - - -const WCHAR szFindName_FindClose_test1[] = {'t','e', 's', 't', '0', '1', '.', 't', 'x', 't', '\0'}; -const WCHAR szFindName_02_FindClose_test1[] = {'t','e', 's', 't', '0', '2', '.', 't', 'x', 't', '\0'}; -const WCHAR szFindName_03_FindClose_test1[] = {'t','e', 's', 't', '0', '3', '.', 't', 'x', 't', '\0'}; -const WCHAR szFindNameWldCard_01_FindClose_test1[] = {'t','e', 's', 't', '0', '?', '.', 't', 'x', 't', '\0'}; -const WCHAR szFindNameWldCard_02_FindClose_test1[] = {'*', '.', 't', 'x', 't', '\0'}; -const WCHAR szDirName_FindClose_test1[] = {'t','e', 's', 't', '_', 'd', 'i', 'r', '\0'}; -const WCHAR szDirName_02_FindClose_test1[] = {'t','e', 's', 't', '_', 'd', 'i', 'r', '0', '2', '\0'}; -const WCHAR szDirNameWldCard_FindClose_test1[] = {'t','e', 's', 't', '_', '*', '\0'}; - - - -BOOL createTestFile_FindClose_test1(const WCHAR* szName) -{ - FILE *pFile = NULL; - char* pTemp = NULL; - - pTemp = convertC((WCHAR*)szName); - pFile = fopen(pTemp, "w"); - if (pFile == NULL) - { - Trace("FindClose: ERROR -> Unable to create file \"%s\".\n", pTemp); - free(pTemp); - return FALSE; - } - else - { - fprintf(pFile, "FindClose test file, \"%s\".\n", pTemp); - free(pTemp); - fclose(pFile); - } - return TRUE; -} - - -void removeAll() -{ - RemoveDirectoryW(szDirName_FindClose_test1); - RemoveDirectoryW(szDirName_02_FindClose_test1); - - DeleteFileW(szFindName_FindClose_test1); - DeleteFileW(szFindName_02_FindClose_test1); - DeleteFileW(szFindName_03_FindClose_test1); -} - - -PALTEST(file_io_FindClose_test1_paltest_findclose_test1, "file_io/FindClose/test1/paltest_findclose_test1") -{ - WIN32_FIND_DATAW findFileData; - WIN32_FIND_DATAW findFileData_02; - HANDLE hFind = NULL; - BOOL bRc = FALSE; - char* pTemp = NULL; - - - if (0 != PAL_Initialize(argc,argv)) - { - return FAIL; - } - - /* do some clean up just to be sure */ - removeAll(); - - /* FindClose a null handle */ - if(FindClose(NULL)!=0) - { - Fail("FindClose: ERROR -> Closing a NULL handle succeeded.\n"); - } - - /* find a file that exists */ - if(createTestFile_FindClose_test1(szFindName_FindClose_test1) == FALSE) - { - removeAll(); - PAL_TerminateEx(FAIL); - return FAIL; - } - if(createTestFile_FindClose_test1(szFindName_02_FindClose_test1) == FALSE) - { - removeAll(); - PAL_TerminateEx(FAIL); - return FAIL; - } - if(createTestFile_FindClose_test1(szFindName_03_FindClose_test1) == FALSE) - { - removeAll(); - PAL_TerminateEx(FAIL); - return FAIL; - } - - // close a FindFirstFileW handle - hFind = FindFirstFileW(szFindName_FindClose_test1, &findFileData); - if (hFind == INVALID_HANDLE_VALUE) - { - pTemp = convertC((WCHAR*)szFindName_FindClose_test1); - Trace("FindClose: ERROR -> Unable to find \"%s\"\n", pTemp); - free(pTemp); - removeAll(); - PAL_TerminateEx(FAIL); - return FAIL; - } - else - { - bRc = FindClose(hFind); - if (bRc == FALSE) - { - removeAll(); - Fail("FindClose: ERROR -> Unable to close a valid" - " FindFirstFileW handle.\n"); - } - } - hFind = FindFirstFileW(szFindName_FindClose_test1, &findFileData); - if (hFind == INVALID_HANDLE_VALUE) - { - pTemp = convertC((WCHAR*)szFindName_FindClose_test1); - Trace("FindClose: ERROR -> Unable to find \"%s\"\n", pTemp); - free(pTemp); - removeAll(); - PAL_TerminateEx(FAIL); - return FAIL; - } - else - { - bRc = FindNextFileW(hFind, &findFileData); - if (bRc != FALSE) - { - removeAll(); - Fail("FindClose: ERROR -> Found a file that doesn't exist.\n"); - } - else - { - bRc = FindClose(hFind); - if (bRc == FALSE) - { - removeAll(); - Fail("FindClose: ERROR -> Unable to close a valid " - "FindNextFileW handle.\n"); - } - } - } - - /* find a directory that exists */ - bRc = CreateDirectoryW(szDirName_FindClose_test1, NULL); - if (bRc == FALSE) - { - pTemp = convertC((WCHAR*)szDirName_FindClose_test1); - Trace("FindClose: ERROR -> Failed to create the directory \"%s\"\n", - pTemp); - free(pTemp); - removeAll(); - PAL_TerminateEx(FAIL); - return FAIL; - } - - bRc = CreateDirectoryW(szDirName_02_FindClose_test1, NULL); - if (bRc == FALSE) - { - pTemp = convertC((WCHAR*)szDirName_02_FindClose_test1); - Trace("FindClose: ERROR -> Failed to create the directory \"%s\"\n", - pTemp); - free(pTemp); - removeAll(); - PAL_TerminateEx(FAIL); - return FAIL; - } - - hFind = FindFirstFileW(szDirName_FindClose_test1, &findFileData); - if (hFind == INVALID_HANDLE_VALUE) - { - pTemp = convertC((WCHAR*)szDirName_FindClose_test1); - Trace("FindClose: ERROR. FindFirstFileW was unable to find \"%s\"\n", - pTemp); - free(pTemp); - removeAll(); - PAL_TerminateEx(FAIL); - return FAIL; - } - else - { - bRc = FindClose(hFind); - if (bRc == FALSE) - { - removeAll(); - Fail("FindClose: ERROR -> Unable to close a valid" - " FindFirstFileW handle of a directory.\n"); - } - } - - /* find a file using wild cards */ - hFind = FindFirstFileW(szFindNameWldCard_01_FindClose_test1, &findFileData); - if (hFind == INVALID_HANDLE_VALUE) - { - pTemp = convertC((WCHAR*)szFindNameWldCard_01_FindClose_test1); - Trace("FindClose: ERROR -> FindFirstFileW was unable to find \"%s\"\n", - pTemp); - free(pTemp); - removeAll(); - PAL_TerminateEx(FAIL); - return FAIL; - } - else - { - bRc = FindNextFileW(hFind, &findFileData_02); - if (bRc == FALSE) - { - removeAll(); - Fail("FindClose: ERROR -> Unable to find another file.\n"); - } - else - { - bRc = FindClose(hFind); - if (bRc == FALSE) - { - removeAll(); - Fail("FindClose: ERROR -> Unable to close a valid" - " FindNextFileW handle.\n"); - } - } - } - - /* find a directory using wild cards */ - hFind = FindFirstFileW(szDirNameWldCard_FindClose_test1, &findFileData); - if (hFind == INVALID_HANDLE_VALUE) - { - pTemp = convertC((WCHAR*)szDirNameWldCard_FindClose_test1); - Trace("FindClose: ERROR -> Unable to find \"%s\"\n", - pTemp); - free(pTemp); - removeAll(); - PAL_TerminateEx(FAIL); - return FAIL; - } - else - { - bRc = FindNextFileW(hFind, &findFileData_02); - if (bRc == FALSE) - { - removeAll(); - Fail("FindClose: ERROR -> Unable to find another directory.\n"); - } - else - { - bRc = FindClose(hFind); - if (bRc == FALSE) - { - removeAll(); - Fail("FindClose: ERROR -> Unable to close a valid" - " FindNextFileW handle of a directory.\n"); - } - } - } - - - removeAll(); - PAL_Terminate(); - - return PASS; -} - diff --git a/src/coreclr/pal/tests/palsuite/file_io/FindFirstFileA/test1/FindFirstFileA.cpp b/src/coreclr/pal/tests/palsuite/file_io/FindFirstFileA/test1/FindFirstFileA.cpp deleted file mode 100644 index 9a4d906162d3b..0000000000000 --- a/src/coreclr/pal/tests/palsuite/file_io/FindFirstFileA/test1/FindFirstFileA.cpp +++ /dev/null @@ -1,207 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: FindFirstFileA.c -** -** Purpose: Tests the PAL implementation of the FindFirstFileA function. -** -** -**===================================================================*/ - - -#include - - -#define szNoFileName "333asdf.x77t" -#define szFindName "test01.txt" -#define szFindNameWldCard_01 "test0?.txt" -#define szFindNameWldCard_02 "*.txt" -#define szDirName "test_dir" -#define szDirNameSlash "test_dir\\" -#define szDirNameWldCard_01 "?est_dir" -#define szDirNameWldCard_02 "test_*" -/* Longer than MAX_LONGPATH characters */ -char szLongFindName[MAX_LONGPATH+1]; - -BOOL CleanUp_FindFirstFileA_test1() -{ - DWORD dwAtt; - BOOL result = TRUE; - - dwAtt = GetFileAttributesA(szFindName); - if( dwAtt != INVALID_FILE_ATTRIBUTES ) - { - if(!SetFileAttributesA (szFindName, FILE_ATTRIBUTE_NORMAL)) - { - result = FALSE; - Trace("ERROR:%d: Error setting attributes [%s][%d]\n", szFindName, FILE_ATTRIBUTE_NORMAL); - } - if(!DeleteFileA (szFindName)) - { - result = FALSE; - Trace("ERROR:%d: Error deleting file [%s][%d]\n", GetLastError(), szFindName, dwAtt); - } - } - - dwAtt = GetFileAttributesA(szDirName); - if( dwAtt != INVALID_FILE_ATTRIBUTES ) - { - LPWSTR szDirNameW = convert(szDirName); - if(!RemoveDirectoryW (szDirNameW)) - { - result = FALSE; - Trace("ERROR:%d: Error deleting file [%s][%d]\n", GetLastError(), szDirName, dwAtt); - } - free(szDirNameW); - } - - return result; -} - -PALTEST(file_io_FindFirstFileA_test1_paltest_findfirstfilea_test1, "file_io/FindFirstFileA/test1/paltest_findfirstfilea_test1") -{ - WIN32_FIND_DATA findFileData; - HANDLE hFind = NULL; - FILE *pFile = NULL; - BOOL bRc = FALSE; - WCHAR* szwTemp = NULL; - - memset(szLongFindName, 'a', MAX_LONGPATH+1); - if (0 != PAL_Initialize(argc,argv)) - { - return FAIL; - } - - - if(!CleanUp_FindFirstFileA_test1()) - { - Fail("FindFirstFileW: ERROR : Initial Clean Up failed\n"); - } - - // - // find a file with a NULL pointer - // - hFind = FindFirstFileA(NULL, &findFileData); - if (hFind != INVALID_HANDLE_VALUE) - { - Fail ("FindFirstFileA: ERROR -> Found invalid NULL file"); - } - - - // - // find a file that doesn't exist - // - hFind = FindFirstFileA(szNoFileName, &findFileData); - if (hFind != INVALID_HANDLE_VALUE) - { - Fail ("FindFirstFileA: ERROR -> Found invalid NULL file"); - } - - - // - // find a file that exists - // - pFile = fopen(szFindName, "w"); - if (pFile == NULL) - { - Fail("FindFirstFileA: ERROR -> Unable to create a test file\n"); - } - else - { - fclose(pFile); - } - hFind = FindFirstFileA(szFindName, &findFileData); - if (hFind == INVALID_HANDLE_VALUE) - { - Fail ("FindFirstFileA: ERROR -> Unable to find \"%s\"\n", szFindName); - } - else - { - // validate we found the correct file - if (strcmp(szFindName, findFileData.cFileName) != 0) - { - Fail ("FindFirstFileA: ERROR -> Found the wrong file\n"); - } - } - - - // - // find a directory that exists - // - szwTemp = convert((LPSTR)szDirName); - bRc = CreateDirectoryW(szwTemp, NULL); - free(szwTemp); - if (bRc == FALSE) - { - Fail("FindFirstFileA: ERROR -> Failed to create the directory " - "\"%s\"\n", - szDirName); - } - - hFind = FindFirstFileA(szDirName, &findFileData); - if (hFind == INVALID_HANDLE_VALUE) - { - Fail ("FindFirstFileA: ERROR. Unable to find \"%s\"\n", szDirName); - } - else - { - // validate we found the correct directory - if (strcmp(szDirName, findFileData.cFileName) != 0) - { - Fail ("FindFirstFileA: ERROR -> Found the wrong directory\n"); - } - } - - - // - // find a directory using a trailing '\' on the directory name: should fail - // - hFind = FindFirstFileA(szDirNameSlash, &findFileData); - if (hFind != INVALID_HANDLE_VALUE) - { - Fail ("FindFirstFileA: ERROR -> Able to find \"%s\": trailing " - "slash should have failed.\n", - szDirNameSlash); - } - - // find a file using wild cards - hFind = FindFirstFileA(szFindNameWldCard_01, &findFileData); - if (hFind == INVALID_HANDLE_VALUE) - { - Fail ("FindFirstFileA: ERROR -> Unable to find \"%s\"\n", - szFindNameWldCard_01); - } - - hFind = FindFirstFileA(szFindNameWldCard_02, &findFileData); - if (hFind == INVALID_HANDLE_VALUE) - { - Fail ("FindFirstFileA: ERROR -> Unable to find \"%s\"\n", szFindNameWldCard_02); - } - - - // - // find a directory using wild cards - // - hFind = FindFirstFileA(szDirNameWldCard_01, &findFileData); - if (hFind == INVALID_HANDLE_VALUE) - { - Fail ("FindFirstFileA: ERROR -> Unable to find \"%s\"\n", szDirNameWldCard_01); - } - - hFind = FindFirstFileA(szDirNameWldCard_02, &findFileData); - if (hFind == INVALID_HANDLE_VALUE) - { - Fail ("FindFirstFileA: ERROR -> Unable to find \"%s\"\n", szDirNameWldCard_02); - } - - if(!CleanUp_FindFirstFileA_test1()) - { - Fail("FindFirstFileW: ERROR : Final Clean Up failed\n"); - } - - PAL_Terminate(); - - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/file_io/FindFirstFileW/test1/FindFirstFileW.cpp b/src/coreclr/pal/tests/palsuite/file_io/FindFirstFileW/test1/FindFirstFileW.cpp deleted file mode 100644 index 362cea7f631ae..0000000000000 --- a/src/coreclr/pal/tests/palsuite/file_io/FindFirstFileW/test1/FindFirstFileW.cpp +++ /dev/null @@ -1,213 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: FindFirstFileW.c -** -** Purpose: Tests the PAL implementation of the FindFirstFileW function. -** -** -**===================================================================*/ - - -#include - - -#define szNoFileName "333asdf.x77t" -#define szFindName "test01.txt" -#define szFindNameWldCard_01 "test0?.txt" -#define szFindNameWldCard_02 "*.txt" -#define szDirName "test_dir" -#define szDirNameSlash "test_dir\\" -#define szDirNameWldCard_01 "?est_dir" -#define szDirNameWldCard_02 "test_*" - - -BOOL CleanUp_FindFirstFileW_test1() -{ - DWORD dwAtt; - BOOL result = TRUE; - - dwAtt = GetFileAttributesA(szFindName); - if( dwAtt != INVALID_FILE_ATTRIBUTES ) - { - if(!SetFileAttributesA (szFindName, FILE_ATTRIBUTE_NORMAL)) - { - result = FALSE; - Trace("ERROR:%d: Error setting attributes [%s][%d]\n", szFindName, FILE_ATTRIBUTE_NORMAL); - } - if(!DeleteFileA (szFindName)) - { - result = FALSE; - Trace("ERROR:%d: Error deleting file [%s][%d]\n", GetLastError(), szFindName, dwAtt); - } - } - - dwAtt = GetFileAttributesA(szDirName); - if( dwAtt != INVALID_FILE_ATTRIBUTES ) - { - LPWSTR szDirNameW = convert(szDirName); - if(!RemoveDirectoryW (szDirNameW)) - { - result = FALSE; - Trace("ERROR:%d: Error deleting file [%s][%d]\n", GetLastError(), szDirName, dwAtt); - } - free(szDirNameW); - } - - return result; -} - -PALTEST(file_io_FindFirstFileW_test1_paltest_findfirstfilew_test1, "file_io/FindFirstFileW/test1/paltest_findfirstfilew_test1") -{ - WIN32_FIND_DATAW findFileData; - HANDLE hFind = NULL; - FILE *pFile = NULL; - BOOL bRc = FALSE; - WCHAR* pTemp = NULL; - - - if (0 != PAL_Initialize(argc,argv)) - { - return FAIL; - } - - if(!CleanUp_FindFirstFileW_test1()) - { - Fail("FindFirstFileW: ERROR : Initial Clean Up failed\n"); - } - - // - // find a file that doesn't exist - // - pTemp = convert((LPSTR)szNoFileName); - hFind = FindFirstFileW(pTemp, &findFileData); - free(pTemp); - if (hFind != INVALID_HANDLE_VALUE) - { - Fail ("FindFirstFileW: ERROR -> Found invalid NULL file\n"); - } - - - // - // find a file that exists - // - pFile = fopen(szFindName, "w"); - if (pFile == NULL) - { - Fail("FindFirstFileW: ERROR -> Unable to create a test file\n"); - } - else - { - fclose(pFile); - } - pTemp = convert((LPSTR)szFindName); - hFind = FindFirstFileW(pTemp, &findFileData); - if (hFind == INVALID_HANDLE_VALUE) - { - free(pTemp); - Fail ("FindFirstFileW: ERROR -> Unable to find \"%s\"\n", szFindName); - } - else - { - // validate we found the correct file - if (wcscmp(pTemp, findFileData.cFileName) != 0) - { - free(pTemp); - Fail ("FindFirstFileW: ERROR -> Found the wrong file\n"); - } - } - free(pTemp); - - // - // find a directory that exists - // - pTemp = convert((LPSTR)szDirName); - bRc = CreateDirectoryW(pTemp, NULL); - if (bRc == FALSE) - { - Fail("FindFirstFileW: ERROR[%u] -> Failed to create the directory \"%s\"\n", - GetLastError(), szDirName); - } - - hFind = FindFirstFileW(pTemp, &findFileData); - if (hFind == INVALID_HANDLE_VALUE) - { - free(pTemp); - Fail("FindFirstFileW: ERROR. Unable to find \"%s\"\n", szDirName); - } - else - { - // validate we found the correct directory - if (wcscmp(pTemp, findFileData.cFileName) != 0) - { - free(pTemp); - Fail("FindFirstFileW: ERROR -> Found the wrong directory\n"); - } - } - free(pTemp); - - // - // find a directory using a trailing '\' on the directory name: should fail - // - pTemp = convert((LPSTR)szDirNameSlash); - hFind = FindFirstFileW(pTemp, &findFileData); - free(pTemp); - if (hFind != INVALID_HANDLE_VALUE) - { - Fail("FindFirstFileW: ERROR -> Able to find \"%s\": trailing " - "slash should have failed.\n", - szDirNameSlash); - } - - // find a file using wild cards - pTemp = convert((LPSTR)szFindNameWldCard_01); - hFind = FindFirstFileW(pTemp, &findFileData); - free(pTemp); - if (hFind == INVALID_HANDLE_VALUE) - { - Fail("FindFirstFileW: ERROR -> Unable to find \"%s\"\n", - szFindNameWldCard_01); - } - - pTemp = convert((LPSTR)szFindNameWldCard_02); - hFind = FindFirstFileW(pTemp, &findFileData); - free(pTemp); - if (hFind == INVALID_HANDLE_VALUE) - { - Fail("FindFirstFileW: ERROR -> Unable to find \"%s\"\n", - szFindNameWldCard_02); - } - - - // - // find a directory using wild cards - // - - pTemp = convert((LPSTR)szDirNameWldCard_01); - hFind = FindFirstFileW(pTemp, &findFileData); - free(pTemp); - if (hFind == INVALID_HANDLE_VALUE) - { - Fail("FindFirstFileW: ERROR -> Unable to find \"%s\"\n", - szDirNameWldCard_01); - } - - pTemp = convert((LPSTR)szDirNameWldCard_02); - hFind = FindFirstFileW(pTemp, &findFileData); - free(pTemp); - if (hFind == INVALID_HANDLE_VALUE) - { - Fail("FindFirstFileW: ERROR -> Unable to find \"%s\"\n", - szDirNameWldCard_02); - } - - if(!CleanUp_FindFirstFileW_test1()) - { - Fail("FindFirstFileW: ERROR : Final Clean Up failed\n"); - } - - PAL_Terminate(); - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/file_io/FindNextFileA/test1/FindNextFileA.cpp b/src/coreclr/pal/tests/palsuite/file_io/FindNextFileA/test1/FindNextFileA.cpp deleted file mode 100644 index ab5c184d13a19..0000000000000 --- a/src/coreclr/pal/tests/palsuite/file_io/FindNextFileA/test1/FindNextFileA.cpp +++ /dev/null @@ -1,242 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: FindNextFileA.c -** -** Purpose: Tests the PAL implementation of the FindNextFileA function. -** -** -**===================================================================*/ - -#include - - -#define szFindName "test01.txt" -#define szFindName_02 "test02.txt" -#define szFindNameWldCard_01 "test0?.txt" -#define szFindNameWldCard_02 "*.txt" -#define szDirName "test_dir" -#define szDirName_02 "test_dir_02" -#define szDirNameWldCard "test_*" - - - -void removeAll_FindNextFileA_test1() -{ - WCHAR* wTempPtr = NULL; - - wTempPtr = convert((LPSTR)szDirName); - RemoveDirectoryW(wTempPtr); - free (wTempPtr); - wTempPtr = convert((LPSTR)szDirName_02); - RemoveDirectoryW(wTempPtr); - free (wTempPtr); - remove(szFindName); - remove(szFindName_02); -} - - - -BOOL createTestFile_FindNextFileA_test1(const char* szName) -{ - FILE *pFile = NULL; - - pFile = fopen(szName, "w"); - if (pFile == NULL) - { - Trace("FindNextFile: ERROR -> Unable to create file \"%s\".\n", - szName); - removeAll_FindNextFileA_test1(); - return FALSE; - } - else - { - fprintf(pFile, "FindNextFile test file, \"%s\".\n", szFindName); - fclose(pFile); - } - return TRUE; -} - - - -PALTEST(file_io_FindNextFileA_test1_paltest_findnextfilea_test1, "file_io/FindNextFileA/test1/paltest_findnextfilea_test1") -{ - WIN32_FIND_DATA findFileData; - WIN32_FIND_DATA findFileData_02; - HANDLE hFind = NULL; - BOOL bRc = FALSE; - DWORD dwBytesWritten; - WCHAR* wTempPtr = NULL; - - - if (0 != PAL_Initialize(argc,argv)) - { - return FAIL; - } - removeAll_FindNextFileA_test1(); - - - // - // find a file with a NULL pointer - // - hFind = FindFirstFileA(NULL, &findFileData); - if (hFind != INVALID_HANDLE_VALUE) - { - Fail("FindNextFile: ERROR -> Found invalid NULL file"); - } - - bRc = FindNextFile(hFind, &findFileData); - if (bRc == TRUE) - { - Fail("FindNextFile: ERROR -> Found a file based on an invalid handle"); - } - - - // - // find a file that exists - // - if(createTestFile_FindNextFileA_test1(szFindName) == FALSE) - { - PAL_TerminateEx(FAIL); - return FAIL; - } - if(createTestFile_FindNextFileA_test1(szFindName_02) == FALSE) - { - PAL_TerminateEx(FAIL); - return FAIL; - } - - hFind = FindFirstFileA(szFindName, &findFileData); - if (hFind == INVALID_HANDLE_VALUE) - { - removeAll_FindNextFileA_test1(); - Fail("FindNextFile: ERROR -> Unable to find \"%s\"\n", szFindName); - } - else - { - bRc = FindNextFile(hFind, &findFileData); - if (bRc != FALSE) - { - removeAll_FindNextFileA_test1(); - Fail("FindNextFile: ERROR -> Found a file that doesn't exist.\n"); - } - } - - - // - // find a directory that exists - // - wTempPtr = convert((LPSTR)szDirName); - bRc = CreateDirectoryW(wTempPtr, NULL); - free (wTempPtr); - if (bRc == FALSE) - { - removeAll_FindNextFileA_test1(); - Fail("FindNextFile: ERROR -> Failed to create the directory \"%s\"\n", - szDirName); - } - wTempPtr = convert((LPSTR)szDirName_02); - bRc = CreateDirectoryW(wTempPtr, NULL); - free (wTempPtr); - if (bRc == FALSE) - { - removeAll_FindNextFileA_test1(); - Fail("FindNextFile: ERROR -> Failed to create the directory \"%s\"\n", - szDirName_02); - } - - hFind = FindFirstFileA(szDirName, &findFileData); - if (hFind == INVALID_HANDLE_VALUE) - { - removeAll_FindNextFileA_test1(); - Fail("FindNextFile: ERROR. FindFirstFileA was unable to find \"%s\"\n", - szDirName); - } - else - { - bRc = FindNextFile(hFind, &findFileData); - if (bRc != FALSE) - { - removeAll_FindNextFileA_test1(); - Fail("FindNextFile: ERROR -> Found a directory that doesn't exist.\n"); - } - } - - - // - // find a file using wild cards - // - hFind = FindFirstFileA(szFindNameWldCard_01, &findFileData); - if (hFind == INVALID_HANDLE_VALUE) - { - removeAll_FindNextFileA_test1(); - Fail("FindNextFile: ERROR -> FindFirstFileA was unable to find \"%s\"\n", - szFindNameWldCard_01); - } - else - { - bRc = FindNextFile(hFind, &findFileData_02); - if (bRc == FALSE) - { - removeAll_FindNextFileA_test1(); - Fail("FindNextFile: ERROR -> Unable to find another file.\n"); - } - else - { - // validate we found the correct file - if (strcmp(findFileData_02.cFileName, findFileData.cFileName) == 0) - { - removeAll_FindNextFileA_test1(); - Fail("FindNextFile: ERROR -> Found the same file \"%s\".\n", - findFileData.cFileName); - } - } - } - - - // - // find a directory using wild cards - // - hFind = FindFirstFileA(szDirNameWldCard, &findFileData); - if (hFind == INVALID_HANDLE_VALUE) - { - removeAll_FindNextFileA_test1(); - Fail("FindNextFile: ERROR -> Unable to find \"%s\"\n", - szDirNameWldCard); - } - else - { - bRc = FindNextFile(hFind, &findFileData_02); - if (bRc == FALSE) - { - removeAll_FindNextFileA_test1(); - Fail("FindNextFile: ERROR -> Unable to find another directory.\n"); - } - else - { - // validate we found the correct directory - if (strcmp(findFileData_02.cFileName, findFileData.cFileName) == 0) - { - removeAll_FindNextFileA_test1(); - Fail("FindNextFile: ERROR -> Found the same directory \"%s\".\n", - findFileData.cFileName); - } - } - } - - // - // attempt to write to the hFind handle (which should fail) - // - bRc = WriteFile(hFind, "this is a test", 10, &dwBytesWritten, NULL); - removeAll_FindNextFileA_test1(); - if (bRc == TRUE) - { - Fail("FindNextFile: ERROR -> Able to write to a FindNextFile handle.\n"); - } - - PAL_Terminate(); - - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/file_io/FindNextFileA/test2/findnextfilea.cpp b/src/coreclr/pal/tests/palsuite/file_io/FindNextFileA/test2/findnextfilea.cpp deleted file mode 100644 index 3ef068ac1af94..0000000000000 --- a/src/coreclr/pal/tests/palsuite/file_io/FindNextFileA/test2/findnextfilea.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: findnextfilea.c -** -** Purpose: Tests the PAL implementation of the FindNextFileA function. -** Tests '*' and '*.*' to ensure that '.' and '..' are -** returned in the expected order -** -** -**===================================================================*/ - -#include - - -const char* szDot = "."; -const char* szDotDot = ".."; -const char* szStar = "*"; -const char* szStarDotStar = "*.*"; - - -static void DoTest(const char* szDir, - const char* szResult1, - const char* szResult2) -{ - HANDLE hFind; - WIN32_FIND_DATA findFileData; - - /* - ** find the first - */ - if ((hFind = FindFirstFileA(szDir, &findFileData)) == INVALID_HANDLE_VALUE) - { - Fail("FindNextFileA: ERROR -> FindFirstFileA(\"%s\") failed. " - "GetLastError returned %u.\n", - szStar, - GetLastError()); - } - - /* did we find the expected */ - if (strcmp(szResult1, findFileData.cFileName) != 0) - { - if (!FindClose(hFind)) - { - Trace("FindNextFileA: ERROR -> Failed to close the find handle. " - "GetLastError returned %u.\n", - GetLastError()); - } - Fail("FindNextFileA: ERROR -> FindFirstFile(\"%s\") didn't find" - " the expected \"%s\" but found \"%s\" instead.\n", - szDir, - szResult1, - findFileData.cFileName); - } - - /* we found the first expected, let's see if we find the next expected*/ - if (!FindNextFileA(hFind, &findFileData)) - { - Trace("FindNextFileA: ERROR -> FindNextFileA should have found \"%s\"" - " but failed. GetLastError returned %u.\n", - szResult2, - GetLastError()); - if (!FindClose(hFind)) - { - Trace("FindNextFileA: ERROR -> Failed to close the find handle. " - "GetLastError returned %u.\n", - GetLastError()); - } - Fail(""); - } - - /* we found something, but was it '.' */ - if (strcmp(szResult2, findFileData.cFileName) != 0) - { - if (!FindClose(hFind)) - { - Trace("FindNextFileA: ERROR -> Failed to close the find handle. " - "GetLastError returned %u.\n", - GetLastError()); - } - Fail("FindNextFileA: ERROR -> FindNextFileA based on \"%s\" didn't find" - " the expected \"%s\" but found \"%s\" instead.\n", - szDir, - szResult2, - findFileData.cFileName); - } -} - -PALTEST(file_io_FindNextFileA_test2_paltest_findnextfilea_test2, "file_io/FindNextFileA/test2/paltest_findnextfilea_test2") -{ - - if (0 != PAL_Initialize(argc,argv)) - { - return FAIL; - } - - DoTest(szStar, szDot, szDotDot); - DoTest(szStarDotStar, szDot, szDotDot); - - - PAL_Terminate(); - - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/file_io/FindNextFileW/test1/FindNextFileW.cpp b/src/coreclr/pal/tests/palsuite/file_io/FindNextFileW/test1/FindNextFileW.cpp deleted file mode 100644 index bdcc73f605f95..0000000000000 --- a/src/coreclr/pal/tests/palsuite/file_io/FindNextFileW/test1/FindNextFileW.cpp +++ /dev/null @@ -1,246 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: FindNextFileW.c -** -** Purpose: Tests the PAL implementation of the FindNextFileW function. -** -** -**===================================================================*/ - -#include - - -#define szFindName "test01.txt" -#define szFindName_02 "test02.txt" -#define szFindNameWldCard_01 "test0?.txt" -#define szFindNameWldCard_02 "*.txt" -#define szDirName "test_dir" -#define szDirName_02 "test_dir_02" -#define szDirNameWldCard "test_*" - -void removeAll_FindNextFileW_test1() -{ - WCHAR* wTempPtr = NULL; - - wTempPtr = convert((LPSTR)szDirName); - RemoveDirectoryW(wTempPtr); - free(wTempPtr); - - wTempPtr = convert((LPSTR)szDirName_02); - RemoveDirectoryW(wTempPtr); - free(wTempPtr); - - wTempPtr = convert((LPSTR)szFindName); - DeleteFileW(wTempPtr); - free(wTempPtr); - - wTempPtr = convert((LPSTR)szFindName_02); - DeleteFileW(wTempPtr); - free(wTempPtr); -} - - - -BOOL createTestFile_FindNextFileW_test1(const char* szName) -{ - FILE *pFile = NULL; - - pFile = fopen(szName, "w"); - if (pFile == NULL) - { - Trace("FindNextFileW: ERROR -> Unable to create file \"%s\".\n", szName); - removeAll_FindNextFileW_test1(); - return FALSE; - } - else - { - fprintf(pFile, "FindNextFileW test file, \"%s\".\n", szFindName); - fclose(pFile); - } - - return TRUE; -} - - - -PALTEST(file_io_FindNextFileW_test1_paltest_findnextfilew_test1, "file_io/FindNextFileW/test1/paltest_findnextfilew_test1") -{ - WIN32_FIND_DATAW findFileData; - WIN32_FIND_DATAW findFileData_02; - HANDLE hFind = NULL; - BOOL bRc = FALSE; - DWORD dwBytesWritten; - WCHAR* wTempPtr = NULL; - - - if (0 != PAL_Initialize(argc,argv)) - { - return FAIL; - } - removeAll_FindNextFileW_test1(); - - - // - // find a file that exists - // - if(createTestFile_FindNextFileW_test1(szFindName) == FALSE) - { - PAL_TerminateEx(FAIL); - return FAIL; - } - if(createTestFile_FindNextFileW_test1(szFindName_02) == FALSE) - { - PAL_TerminateEx(FAIL); - return FAIL; - } - - wTempPtr = convert((LPSTR)szFindName); - hFind = FindFirstFileW(wTempPtr, &findFileData); - free(wTempPtr); - if (hFind == INVALID_HANDLE_VALUE) - { - removeAll_FindNextFileW_test1(); - Fail("FindNextFileW: ERROR -> Unable to find \"%s\"\n", szFindName); - } - else - { - bRc = FindNextFileW(hFind, &findFileData); - if (bRc != FALSE) - { - removeAll_FindNextFileW_test1(); - Fail("FindNextFileW: ERROR -> Found a file that doesn't exist.\n"); - } - } - - - // - // find a directory that exists - // - wTempPtr = convert((LPSTR)szDirName); - bRc = CreateDirectoryW(wTempPtr, NULL); - free (wTempPtr); - if (bRc == FALSE) - { - removeAll_FindNextFileW_test1(); - Fail("FindNextFileW: ERROR -> Failed to create the directory \"%s\"\n", - szDirName); - } - wTempPtr = convert((LPSTR)szDirName_02); - bRc = CreateDirectoryW(wTempPtr, NULL); - free (wTempPtr); - if (bRc == FALSE) - { - removeAll_FindNextFileW_test1(); - Fail("FindNextFileW: ERROR -> Failed to create the directory " - "\"%s\"\n", - szDirName_02); - } - - wTempPtr = convert((LPSTR)szDirName); - hFind = FindFirstFileW(wTempPtr, &findFileData); - free (wTempPtr); - if (hFind == INVALID_HANDLE_VALUE) - { - removeAll_FindNextFileW_test1(); - Fail("FindNextFileW: ERROR. FindFirstFileW was unable " - "to find \"%s\"\n", - szDirName); - } - else - { - bRc = FindNextFileW(hFind, &findFileData); - if (bRc != FALSE) - { - removeAll_FindNextFileW_test1(); - Fail("FindNextFileW: ERROR -> Found a directory that " - "doesn't exist.\n"); - } - } - - - // - // find a file using wild cards - // - wTempPtr = convert((LPSTR)szFindNameWldCard_01); - hFind = FindFirstFileW(wTempPtr, &findFileData); - free(wTempPtr); - if (hFind == INVALID_HANDLE_VALUE) - { - removeAll_FindNextFileW_test1(); - Fail("FindNextFileW: ERROR -> FindFirstFileW was unable to " - "find \"%s\"\n", - szFindNameWldCard_01); - } - else - { - bRc = FindNextFileW(hFind, &findFileData_02); - if (bRc == FALSE) - { - removeAll_FindNextFileW_test1(); - Fail("FindNextFileW: ERROR -> Unable to find another file.\n"); - } - else - { - // validate we found the correct file - if (wcscmp(findFileData_02.cFileName, findFileData.cFileName) == 0) - { - removeAll_FindNextFileW_test1(); - Fail("FindNextFileW: ERROR -> Found the same file \"%S\".\n", - findFileData.cFileName); - } - } - } - - - // - // find a directory using wild cards - // - wTempPtr = convert((LPSTR)szDirNameWldCard); - hFind = FindFirstFileW(wTempPtr, &findFileData); - free(wTempPtr); - if (hFind == INVALID_HANDLE_VALUE) - { - removeAll_FindNextFileW_test1(); - Fail("FindNextFileW: ERROR -> Unable to find \"%s\"\n", - szDirNameWldCard); - } - else - { - bRc = FindNextFileW(hFind, &findFileData_02); - if (bRc == FALSE) - { - removeAll_FindNextFileW_test1(); - Fail("FindNextFileW: ERROR -> Unable to find another directory.\n"); - } - else - { - // validate we found the correct directory - if (wcscmp(findFileData_02.cFileName, findFileData.cFileName) == 0) - { - removeAll_FindNextFileW_test1(); - Fail("FindNextFileW: ERROR -> Found the same directory " - "\"%S\".\n", - findFileData.cFileName); - } - } - } - - // - // attempt to write to the hFind handle (which should fail) - // - bRc = WriteFile(hFind, "this is a test", 10, &dwBytesWritten, NULL); - if (bRc == TRUE) - { - removeAll_FindNextFileW_test1(); - Fail("FindNextFileW: ERROR -> Able to write to a FindNextFileW " - "handle.\n"); - } - - removeAll_FindNextFileW_test1(); - PAL_Terminate(); - - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/file_io/FindNextFileW/test2/findnextfilew.cpp b/src/coreclr/pal/tests/palsuite/file_io/FindNextFileW/test2/findnextfilew.cpp deleted file mode 100644 index 58d01da7e8f02..0000000000000 --- a/src/coreclr/pal/tests/palsuite/file_io/FindNextFileW/test2/findnextfilew.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -/*===================================================================== -** -** Source: FindNextFileW.c -** -** Purpose: Tests the PAL implementation of the FindNextFileW function. -** Tests '*' and '*.*' to ensure that '.' and '..' are -** returned in the expected order -** -** -**===================================================================*/ - -#include - - -const WCHAR szwDot[] = {'.','\0'}; -const WCHAR szwDotDot[] = {'.','.','\0'}; -const WCHAR szwStar[] = {'*','\0'}; -const WCHAR szwStarDotStar[] = {'*','.','*','\0'}; - - -static void DoTest(const WCHAR* szwDir, - const WCHAR* szwResult1, - const WCHAR* szwResult2) -{ - HANDLE hFind; - WIN32_FIND_DATAW findFileData; - - /* - ** find the first - */ - if ((hFind = FindFirstFileW(szwDir, &findFileData)) == INVALID_HANDLE_VALUE) - { - Fail("FindNextFileW: ERROR -> FindFirstFileW(\"%S\") failed. " - "GetLastError returned %u.\n", - szwStar, - GetLastError()); - } - - /* did we find the expected */ - if (wcscmp(szwResult1, findFileData.cFileName) != 0) - { - if (!FindClose(hFind)) - { - Trace("FindNextFileW: ERROR -> Failed to close the find handle. " - "GetLastError returned %u.\n", - GetLastError()); - } - Fail("FindNextFileW: ERROR -> FindFirstFile(\"%S\") didn't find" - " the expected \"%S\" but found \"%S\" instead.\n", - szwDir, - szwResult1, - findFileData.cFileName); - } - - /* we found the first expected, let's see if we find the next expected*/ - if (!FindNextFileW(hFind, &findFileData)) - { - Trace("FindNextFileW: ERROR -> FindNextFileW should have found \"%S\"" - " but failed. GetLastError returned %u.\n", - szwResult2, - GetLastError()); - if (!FindClose(hFind)) - { - Trace("FindNextFileW: ERROR -> Failed to close the find handle. " - "GetLastError returned %u.\n", - GetLastError()); - } - Fail(""); - } - - /* we found something, but was it '.' */ - if (wcscmp(szwResult2, findFileData.cFileName) != 0) - { - if (!FindClose(hFind)) - { - Trace("FindNextFileW: ERROR -> Failed to close the find handle. " - "GetLastError returned %u.\n", - GetLastError()); - } - Fail("FindNextFileW: ERROR -> FindNextFileW based on \"%S\" didn't find" - " the expected \"%S\" but found \"%S\" instead.\n", - szwDir, - szwResult2, - findFileData.cFileName); - } -} - -PALTEST(file_io_FindNextFileW_test2_paltest_findnextfilew_test2, "file_io/FindNextFileW/test2/paltest_findnextfilew_test2") -{ - - if (0 != PAL_Initialize(argc,argv)) - { - return FAIL; - } - - DoTest(szwStar, szwDot, szwDotDot); - DoTest(szwStarDotStar, szwDot, szwDotDot); - - - PAL_Terminate(); - - return PASS; -} diff --git a/src/coreclr/pal/tests/palsuite/file_io/errorpathnotfound/test2/test2.cpp b/src/coreclr/pal/tests/palsuite/file_io/errorpathnotfound/test2/test2.cpp index 927f800c1e43f..034fdfdccaf79 100644 --- a/src/coreclr/pal/tests/palsuite/file_io/errorpathnotfound/test2/test2.cpp +++ b/src/coreclr/pal/tests/palsuite/file_io/errorpathnotfound/test2/test2.cpp @@ -17,8 +17,6 @@ ** Functions covered by this test are: -** FindFirstFileA, FindFirstFileW, - ** GetFileAttributesA, GetFileAttributesW, ** @@ -50,10 +48,6 @@ PALTEST(file_io_errorpathnotfound_test2_paltest_errorpathnotfound_test2, "file_i HANDLE hFile; - WIN32_FIND_DATA findFileData; - - WIN32_FIND_DATAW wFindFileData; - DWORD fileAttrib; @@ -91,253 +85,7 @@ PALTEST(file_io_errorpathnotfound_test2_paltest_errorpathnotfound_test2, "file_i } - - /*............. Test FindFirstFileA..................................*/ - - - - /* test with an invalid file name */ - - hFile = FindFirstFileA(sBadFileName,&findFileData ); - - - - if (hFile == INVALID_HANDLE_VALUE) - - { - - if(GetLastError() != ERROR_FILE_NOT_FOUND) - - { - - Trace("FindFirstFileA: calling GetLastError() returned [%u] " - - "while it should return [%u] for a bad File Name\n", - - GetLastError(),ERROR_FILE_NOT_FOUND); - - testPass = FALSE; - - } - - - - } - - else - - { - - Trace("FindFirstFileA: managed to find a file with an incorrect " - - "filename\n"); - - testPass = FALSE; - - - - if(!FindClose(hFile)) - - { - - Trace("FindFirstFileA: Call to FindClose failed with ErrorCode" - - " [%u]\n", GetLastError()); - - - - } - - - - } - - - - /* test with an invalid path */ - - hFile = FindFirstFileA(sBadFilePath,&findFileData); - - - - if (hFile == INVALID_HANDLE_VALUE) - - { - - if(GetLastError() != ERROR_PATH_NOT_FOUND) - - { - - Trace("FindFirstFileA: calling GetLastError() returned [%u] " - - "while it should return [%u] for a bad file path name\n", - - GetLastError(), ERROR_PATH_NOT_FOUND); - - testPass = FALSE; - - } - - - - } - - else - - { - - Trace("FindFirstFileA: managed to find a file with an incorrect" - - " filename\n"); - - testPass = FALSE; - - /*this should not happen*/ - - if(!FindClose(hFile)) - - { - - Trace("FindFirstFileA: Call to FindClose Failed with ErrorCode" - - " [%u]\n", GetLastError()); - - - - } - - - - } - - - - - - - - /*............. Test FindFirstFileW..................................*/ - - - - /* test with an invalid file name */ - - hFile = FindFirstFileW(wBadFileName,&wFindFileData ); - - - - if (hFile == INVALID_HANDLE_VALUE) - - { - - if(GetLastError() != ERROR_FILE_NOT_FOUND) - - { - - Trace("FindFirstFileW: calling GetLastError() returned [%u] " - - "while it should return [%u] for a bad File Name\n", - - GetLastError(),ERROR_FILE_NOT_FOUND); - - testPass = FALSE; - - } - - - - } - - else - - { - - Trace("FindFirstFileW: managed to find a file with an incorrect " - - "filename\n"); - - testPass = FALSE; - - - - if(!FindClose(hFile)) - - { - - Trace("FindFirstFileW: Call to FindClose failed with ErrorCode" - - " [%u]\n", GetLastError()); - - - - } - - - - } - - - - /* test with an invalid path */ - - hFile = FindFirstFileW(wBadFilePath,&wFindFileData); - - - - if (hFile == INVALID_HANDLE_VALUE) - - { - - if(GetLastError() != ERROR_PATH_NOT_FOUND) - - { - - Trace("FindFirstFileW: calling GetLastError() returned [%u] " - - "while it should return [%u] for a bad file path name\n", - - GetLastError(), ERROR_PATH_NOT_FOUND); - - testPass = FALSE; - - } - - - - } - - else - - { - - Trace("FindFirstFileW: managed to find a file with an incorrect " - - "filename\n"); - - testPass = FALSE; - - /*this should not happen*/ - - if(!FindClose(hFile)) - - { - - Trace("FindFirstFileW: Call to FindClose Failed with ErrorCode " - - "[%u]\n", GetLastError()); - - - - } - - - - } - - - - - + /*...................Test GetFileAttributesW.............................*/ diff --git a/src/coreclr/pal/tests/palsuite/paltestlist.txt b/src/coreclr/pal/tests/palsuite/paltestlist.txt index b5125fa622b1c..d8fd3a875739c 100644 --- a/src/coreclr/pal/tests/palsuite/paltestlist.txt +++ b/src/coreclr/pal/tests/palsuite/paltestlist.txt @@ -160,13 +160,6 @@ file_io/CopyFileW/test2/paltest_copyfilew_test2 file_io/CopyFileW/test3/paltest_copyfilew_test3 file_io/errorpathnotfound/test2/paltest_errorpathnotfound_test2 file_io/FILECanonicalizePath/paltest_filecanonicalizepath_test1 -file_io/FindClose/test1/paltest_findclose_test1 -file_io/FindFirstFileA/test1/paltest_findfirstfilea_test1 -file_io/FindFirstFileW/test1/paltest_findfirstfilew_test1 -file_io/FindNextFileA/test1/paltest_findnextfilea_test1 -file_io/FindNextFileA/test2/paltest_findnextfilea_test2 -file_io/FindNextFileW/test1/paltest_findnextfilew_test1 -file_io/FindNextFileW/test2/paltest_findnextfilew_test2 file_io/FlushFileBuffers/test1/paltest_flushfilebuffers_test1 file_io/GetConsoleOutputCP/test1/paltest_getconsoleoutputcp_test1 file_io/GetFileAttributesA/test1/paltest_getfileattributesa_test1 diff --git a/src/coreclr/pal/tests/palsuite/paltestlist_to_be_reviewed.txt b/src/coreclr/pal/tests/palsuite/paltestlist_to_be_reviewed.txt index a8e4cd0e7831c..964540f7dc90d 100644 --- a/src/coreclr/pal/tests/palsuite/paltestlist_to_be_reviewed.txt +++ b/src/coreclr/pal/tests/palsuite/paltestlist_to_be_reviewed.txt @@ -120,8 +120,6 @@ threading/setthreadcontext/test1/paltest_setthreadcontext_test1 threading/Sleep/test1/paltest_sleep_test1 threading/SleepEx/test1/paltest_sleepex_test1 threading/SleepEx/test2/paltest_sleepex_test2 -threading/SuspendThread/test2/paltest_suspendthread_test2 -threading/SuspendThread/test3/paltest_suspendthread_test3 threading/TerminateProcess/test1/paltest_terminateprocess_test1 threading/TLS/test6_optimizedtls/paltest_tls_test6_optimizedtls threading/WaitForMultipleObjectsEx/test5/paltest_waitformultipleobjectsex_test5 diff --git a/src/coreclr/tools/superpmi/mcs/verbmerge.cpp b/src/coreclr/tools/superpmi/mcs/verbmerge.cpp index 0ee4201311964..e85997864188d 100644 --- a/src/coreclr/tools/superpmi/mcs/verbmerge.cpp +++ b/src/coreclr/tools/superpmi/mcs/verbmerge.cpp @@ -7,6 +7,13 @@ #include "logging.h" #include "spmiutil.h" #include +#ifdef TARGET_UNIX +#include +#include +#include +#endif + +#include // Do reads/writes in large 256MB chunks. #define BUFFER_SIZE 0x10000000 @@ -39,6 +46,15 @@ char* verbMerge::ConvertWideCharToMultiByte(LPCWSTR wstr) return encodedStr; } +WCHAR* verbMerge::ConvertMultiByteToWideChar(LPCSTR str) +{ + unsigned int codePage = CP_UTF8; + int sizeNeeded = MultiByteToWideChar(codePage, 0, str, -1, NULL, 0); + WCHAR* encodedStr = new WCHAR[sizeNeeded]; + MultiByteToWideChar(codePage, 0, str, -1, encodedStr, sizeNeeded); + return encodedStr; +} + // AppendFileRaw: append the file named by 'fileFullPath' to the output file referred to by 'hFileOut'. The 'hFileOut' // handle is assumed to be open, and the file position is assumed to be at the correct spot for writing, to append. // The file is simply appended. @@ -144,8 +160,9 @@ int verbMerge::AppendFile(HANDLE hFileOut, LPCWSTR fileFullPath, bool dedup, uns // Return true if this is a directory // // static -bool verbMerge::DirectoryFilterDirectories(WIN32_FIND_DATAW* findData) +bool verbMerge::DirectoryFilterDirectories(FilterArgType* findData) { +#ifdef TARGET_WINDOWS if ((findData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { // It's a directory. See if we want to exclude it because of other reasons, such as: @@ -153,11 +170,8 @@ bool verbMerge::DirectoryFilterDirectories(WIN32_FIND_DATAW* findData) // 2. system directories // 3. hidden directories // 4. "." or ".." - -#ifndef TARGET_UNIX // FILE_ATTRIBUTE_REPARSE_POINT is not defined in the PAL if ((findData->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) return false; -#endif // !TARGET_UNIX if ((findData->dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) != 0) return false; if ((findData->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0) @@ -170,6 +184,17 @@ bool verbMerge::DirectoryFilterDirectories(WIN32_FIND_DATAW* findData) return true; } +#else // TARGET_WINDOWS + if (findData->d_type == DT_DIR) + { + if (strcmp(findData->d_name, ".") == 0) + return false; + if (strcmp(findData->d_name, "..") == 0) + return false; + + return true; + } +#endif // TARGET_WINDOWS return false; } @@ -177,9 +202,13 @@ bool verbMerge::DirectoryFilterDirectories(WIN32_FIND_DATAW* findData) // Return true if this is a file. // // static -bool verbMerge::DirectoryFilterFile(WIN32_FIND_DATAW* findData) +bool verbMerge::DirectoryFilterFile(FilterArgType* findData) { +#ifdef TARGET_WINDOWS if ((findData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) +#else // TARGET_WINDOWS + if (findData->d_type != DT_DIR) +#endif // TARGET_WINDOWS { // This is not a directory, so it must be a file. return true; @@ -189,10 +218,10 @@ bool verbMerge::DirectoryFilterFile(WIN32_FIND_DATAW* findData) } // static -int __cdecl verbMerge::WIN32_FIND_DATAW_qsort_helper(const void* p1, const void* p2) +int __cdecl verbMerge::FindData_qsort_helper(const void* p1, const void* p2) { - const WIN32_FIND_DATAW* file1 = (WIN32_FIND_DATAW*)p1; - const WIN32_FIND_DATAW* file2 = (WIN32_FIND_DATAW*)p2; + const FindData* file1 = (FindData*)p1; + const FindData* file2 = (FindData*)p2; return u16_strcmp(file1->cFileName, file2->cFileName); } @@ -204,15 +233,18 @@ int __cdecl verbMerge::WIN32_FIND_DATAW_qsort_helper(const void* p1, const void* // If success, fileArray and elemCount are set. // // static -int verbMerge::FilterDirectory(LPCWSTR searchPattern, +int verbMerge::FilterDirectory(LPCWSTR dir, + LPCWSTR searchPattern, DirectoryFilterFunction_t filter, - /* out */ WIN32_FIND_DATAW** ppFileArray, + /* out */ FindData** ppFileArray, int* pElemCount) { + + // First, build up a list, then create an array and sort it after we know how many elements there are. struct findDataList { - findDataList(WIN32_FIND_DATAW* newFindData, findDataList* newNext) : findData(*newFindData), next(newNext) + findDataList(FindData* newFindData, findDataList* newNext) : findData(std::move(*newFindData)), next(newNext) { } @@ -226,31 +258,72 @@ int verbMerge::FilterDirectory(LPCWSTR searchPattern, } } - WIN32_FIND_DATAW findData; + FindData findData; findDataList* next; }; - WIN32_FIND_DATAW* retArray = nullptr; + FindData* retArray = nullptr; findDataList* first = nullptr; int result = 0; // default to zero == success int elemCount = 0; // NOTE: this function only works on Windows 7 and later. - WIN32_FIND_DATAW findData; - HANDLE hSearch; + #ifdef TARGET_UNIX - // PAL doesn't have FindFirstFileEx(). So just use FindFirstFile(). The only reason we use - // the Ex version is potentially better performance (don't populate short name; use large fetch), - // not functionality. - hSearch = FindFirstFileW(searchPattern, &findData); + std::string dirUtf8 = ConvertToUtf8(dir); + std::string searchPatternUtf8 = ConvertToUtf8(searchPattern); + + DIR* pDir = opendir(dirUtf8.c_str()); + if (pDir != nullptr) + { + errno = 0; + dirent *pEntry = readdir(pDir); + while (pEntry != nullptr) + { + if ((fnmatch(searchPatternUtf8.c_str(), pEntry->d_name, 0) == 0) && filter(pEntry)) + { + FindData findData(pEntry->d_type, ConvertMultiByteToWideChar(pEntry->d_name)); + first = new findDataList(&findData, first); + ++elemCount; + } + errno = 0; + pEntry = readdir(pDir); + } + + if (errno != 0) + { + LogError("Failed to read directory. errno=%d", errno); + result = -1; + goto CLEAN_UP; + } + } + else + { + if (errno == ENOENT) + { + // This is ok; there was just nothing matching the pattern. + } + else + { + std::string searchPatternUtf8 = ConvertToUtf8(searchPattern); + LogError("Failed to find pattern '%s'. errno=%d", searchPatternUtf8.c_str(), errno); + result = -1; + } + goto CLEAN_UP; + } + #else // !TARGET_UNIX - hSearch = FindFirstFileExW(searchPattern, + FindData findData; + HANDLE hSearch; + LPWSTR completeSearchPattern = MergePathStrings(dir, searchPattern); + hSearch = FindFirstFileExW(completeSearchPattern, FindExInfoBasic, // We don't care about the short names &findData, FindExSearchNameMatch, // standard name matching NULL, FIND_FIRST_EX_LARGE_FETCH); -#endif // !TARGET_UNIX + + delete [] completeSearchPattern; if (hSearch == INVALID_HANDLE_VALUE) { @@ -292,30 +365,39 @@ int verbMerge::FilterDirectory(LPCWSTR searchPattern, break; } } +#endif // !TARGET_UNIX // Now sort the list. Create an array to put everything in. int i; - retArray = new WIN32_FIND_DATAW[elemCount]; + retArray = new FindData[elemCount]; i = 0; for (findDataList* tmp = first; tmp != nullptr; tmp = tmp->next) { - retArray[i++] = tmp->findData; + retArray[i++] = std::move(tmp->findData); } - qsort(retArray, elemCount, sizeof(retArray[0]), WIN32_FIND_DATAW_qsort_helper); + qsort(retArray, elemCount, sizeof(retArray[0]), FindData_qsort_helper); CLEAN_UP: findDataList::DeleteList(first); - +#ifdef TARGET_WINDOWS if ((hSearch != INVALID_HANDLE_VALUE) && !FindClose(hSearch)) { LogError("Failed to close search handle. GetLastError()=%u", GetLastError()); delete[] retArray; return -1; } +#else // TARGET_WINDOWS + if (pDir != nullptr && (closedir(pDir) != 0)) + { + LogError("Failed to close directory. errno=%d", errno); + delete[] retArray; + return -1; + } +#endif // TARGET_WINDOWS *ppFileArray = retArray; *pElemCount = elemCount; @@ -337,11 +419,9 @@ int verbMerge::AppendAllInDir(HANDLE hFileOut, int result = 0; // default to zero == success LONGLONG totalSize = 0; - LPWSTR searchPattern = MergePathStrings(dir, file); - - _WIN32_FIND_DATAW* fileArray = nullptr; + FindData* fileArray = nullptr; int elemCount = 0; - result = FilterDirectory(searchPattern, DirectoryFilterFile, &fileArray, &elemCount); + result = FilterDirectory(dir, file, DirectoryFilterFile, &fileArray, &elemCount); if (result != 0) { goto CLEAN_UP; @@ -349,8 +429,9 @@ int verbMerge::AppendAllInDir(HANDLE hFileOut, for (int i = 0; i < elemCount; i++) { - const _WIN32_FIND_DATAW& findData = fileArray[i]; - LPWSTR fileFullPath = MergePathStrings(dir, findData.cFileName); + const FindData& findData = fileArray[i]; + LPWSTR fileFullPath = MergePathStrings(dir, findData.cFileName); + #ifdef TARGET_WINDOWS if (u16_strlen(fileFullPath) > MAX_PATH) // This path is too long, use \\?\ to access it. { @@ -380,10 +461,27 @@ int verbMerge::AppendAllInDir(HANDLE hFileOut, fileFullPath = newBuffer; } + + uint64_t fileSize = ((uint64_t)findData.nFileSizeHigh << 32) | findData.nFileSizeLow; + +#else // TARGET_WINDOWS + struct stat fileStat; + char *fileFullPathUtf8 = ConvertWideCharToMultiByte(fileFullPath); + int st = stat(fileFullPathUtf8, &fileStat); + if (st != 0) + { + LogError("Failed to stat file '%s'. errno=%d", fileFullPathUtf8, errno); + result = -1; + delete[] fileFullPath; + delete[] fileFullPathUtf8; + goto CLEAN_UP; + } + delete[] fileFullPathUtf8; + uint64_t fileSize = fileStat.st_size; #endif // TARGET_WINDOWS // Is it zero length? If so, skip it. - if ((findData.nFileSizeLow == 0) && (findData.nFileSizeHigh == 0)) + if (fileSize == 0) { char* fileFullPathAsChar = ConvertWideCharToMultiByte(fileFullPath); LogInfo("Skipping zero-length file '%s'", fileFullPathAsChar); @@ -401,19 +499,17 @@ int verbMerge::AppendAllInDir(HANDLE hFileOut, } delete[] fileFullPath; - totalSize += ((LONGLONG)findData.nFileSizeHigh << 32) + (LONGLONG)findData.nFileSizeLow; + totalSize += fileSize; } // If we need to recurse, then search the directory again for directories, and recursively search each one. if (recursive) { - delete[] searchPattern; delete[] fileArray; - searchPattern = MergePathStrings(dir, W("*")); fileArray = nullptr; elemCount = 0; - result = FilterDirectory(searchPattern, DirectoryFilterDirectories, &fileArray, &elemCount); + result = FilterDirectory(dir, W("*"), DirectoryFilterDirectories, &fileArray, &elemCount); if (result != 0) { goto CLEAN_UP; @@ -422,11 +518,11 @@ int verbMerge::AppendAllInDir(HANDLE hFileOut, LONGLONG dirSize = 0; for (int i = 0; i < elemCount; i++) { - const _WIN32_FIND_DATAW& findData = fileArray[i]; + const FindData& findData = fileArray[i]; + LPWSTR subDir = MergePathStrings(dir, findData.cFileName); + result = AppendAllInDir(hFileOut, subDir, file, buffer, bufferSize, recursive, dedup, &dirSize); + delete [] subDir; - LPWSTR fileFullPath = MergePathStrings(dir, findData.cFileName); - result = AppendAllInDir(hFileOut, fileFullPath, file, buffer, bufferSize, recursive, dedup, &dirSize); - delete[] fileFullPath; if (result != 0) { // Error was already logged. @@ -439,7 +535,6 @@ int verbMerge::AppendAllInDir(HANDLE hFileOut, CLEAN_UP: - delete[] searchPattern; delete[] fileArray; if (result == 0) diff --git a/src/coreclr/tools/superpmi/mcs/verbmerge.h b/src/coreclr/tools/superpmi/mcs/verbmerge.h index 9891d9bd55e44..16a326bd37291 100644 --- a/src/coreclr/tools/superpmi/mcs/verbmerge.h +++ b/src/coreclr/tools/superpmi/mcs/verbmerge.h @@ -10,24 +10,78 @@ #include "removedup.h" +#ifdef TARGET_WINDOWS +typedef _WIN32_FIND_DATAW FindData; +#else +struct FindData +{ + unsigned char d_type; + WCHAR *cFileName; + + FindData() : d_type(0), cFileName(nullptr) + { + } + + FindData(unsigned char type, WCHAR *fileName) + { + d_type = type; + cFileName = fileName; + } + + // Prevent copying of the FindData, only allow moving so that the cFileName doesn't need to be duplicated. + FindData(const FindData& other) = delete; + FindData& operator=(const FindData& other) = delete; + + FindData(FindData&& other) : d_type(other.d_type), cFileName(other.cFileName) + { + other.d_type = 0; + other.cFileName = nullptr; + } + + FindData& operator=(FindData&& other) + { + d_type = other.d_type; + cFileName = other.cFileName; + other.d_type = 0; + other.cFileName = nullptr; + + return *this; + } + + ~FindData() + { + delete [] cFileName; + } +}; +#endif + class verbMerge { public: static int DoWork(const char* nameOfOutputFile, const char* pattern, bool recursive, bool dedup, bool stripCR); private: - typedef bool (*DirectoryFilterFunction_t)(WIN32_FIND_DATAW*); - static bool DirectoryFilterDirectories(WIN32_FIND_DATAW* findData); - static bool DirectoryFilterFile(WIN32_FIND_DATAW* findData); - static int __cdecl WIN32_FIND_DATAW_qsort_helper(const void* p1, const void* p2); - static int FilterDirectory(LPCWSTR searchPattern, + +#ifdef TARGET_WINDOWS + typedef _WIN32_FIND_DATAW FilterArgType; +#else + typedef struct dirent FilterArgType; +#endif + + typedef bool (*DirectoryFilterFunction_t)(FilterArgType*); + static bool DirectoryFilterDirectories(FilterArgType* findData); + static bool DirectoryFilterFile(FilterArgType* findData); + static int __cdecl FindData_qsort_helper(const void* p1, const void* p2); + static int FilterDirectory(LPCWSTR dir, + LPCWSTR searchPattern, DirectoryFilterFunction_t filter, - /* out */ WIN32_FIND_DATAW** ppFileArray, + /* out */ FindData** ppFileArray, int* pElemCount); static LPWSTR MergePathStrings(LPCWSTR dir, LPCWSTR file); static char* ConvertWideCharToMultiByte(LPCWSTR wstr); + static WCHAR* ConvertMultiByteToWideChar(LPCSTR str); static int AppendFileRaw(HANDLE hFileOut, LPCWSTR fileFullPath, unsigned char* buffer, size_t bufferSize); static int AppendFile(HANDLE hFileOut, LPCWSTR fileFullPath, bool dedup, unsigned char* buffer, size_t bufferSize); From 4822cc017a578c7b1d08ce6cb11d3c91690215b5 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 18 Dec 2024 13:01:26 -0800 Subject: [PATCH 14/25] JIT: revise how we track if nodes have been morphed (#110787) Allow remorphing. Require that all nodes be morphed. Ensure that no node is morphed more than a handful of times. Fixes #6218, #56962, #107542 --- src/coreclr/jit/compiler.h | 6 +- src/coreclr/jit/compiler.hpp | 1 + src/coreclr/jit/gentree.cpp | 151 ++++++++++-------- src/coreclr/jit/gentree.h | 43 ++++-- src/coreclr/jit/morph.cpp | 272 +++++++++++++++++---------------- src/coreclr/jit/morphblock.cpp | 38 +++-- src/coreclr/jit/optimizer.cpp | 4 +- 7 files changed, 293 insertions(+), 222 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 5160d287cb311..d99813fe5290f 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -5438,6 +5438,10 @@ class Compiler void fgMorphBlock(BasicBlock* block, MorphUnreachableInfo* unreachableInfo = nullptr); void fgMorphStmts(BasicBlock* block); +#ifdef DEBUG + void fgPostGlobalMorphChecks(); +#endif + void fgMergeBlockReturn(BasicBlock* block); bool fgMorphBlockStmt(BasicBlock* block, Statement* stmt DEBUGARG(const char* msg), bool invalidateDFSTreeOnFGChange = true); @@ -6785,7 +6789,7 @@ class Compiler void fgKillDependentAssertionsSingle(unsigned lclNum DEBUGARG(GenTree* tree)); void fgKillDependentAssertions(unsigned lclNum DEBUGARG(GenTree* tree)); void fgMorphTreeDone(GenTree* tree); - void fgMorphTreeDone(GenTree* tree, bool optAssertionPropDone, bool isMorphedTree DEBUGARG(int morphNum = 0)); + void fgMorphTreeDone(GenTree* tree, bool optAssertionPropDone DEBUGARG(int morphNum = 0)); Statement* fgMorphStmt; unsigned fgBigOffsetMorphingTemps[TYP_COUNT]; diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 38fa10167c48a..1494bd39aee2a 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -1354,6 +1354,7 @@ inline GenTree::GenTree(genTreeOps oper, var_types type DEBUGARG(bool largeNode) gtLIRFlags = 0; #ifdef DEBUG gtDebugFlags = GTF_DEBUG_NONE; + gtMorphCount = 0; #endif // DEBUG gtCSEnum = NO_CSE; ClearAssertion(); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index d8d5752a5f644..f48cbc4e02dfd 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -4903,7 +4903,6 @@ static void SetIndirectStoreEvalOrder(Compiler* comp, GenTreeIndir* store, bool* * 1. GetCostEx() to the execution complexity estimate * 2. GetCostSz() to the code size estimate * 3. Sometimes sets GTF_ADDRMODE_NO_CSE on nodes in the tree. - * 4. DEBUG-only: clears GTF_DEBUG_NODE_MORPHED. */ #ifdef _PREFAST_ @@ -14388,10 +14387,7 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree) // Helper function that creates a new IntCon node and morphs it, if required auto NewMorphedIntConNode = [&](int value) -> GenTreeIntCon* { GenTreeIntCon* icon = gtNewIconNode(value); - if (fgGlobalMorph) - { - INDEBUG(icon->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); - } + icon->SetMorphed(this); return icon; }; @@ -14402,18 +14398,14 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree) assert(varTypeIsUnsigned(castToType)); GenTreeCast* cast = gtNewCastNode(TYP_INT, op1, false, castToType); - if (fgGlobalMorph) - { - fgMorphTreeDone(cast); - } + cast->SetMorphed(this); + fgMorphTreeDone(cast); if (type == TYP_LONG) { cast = gtNewCastNode(TYP_LONG, cast, true, TYP_LONG); - if (fgGlobalMorph) - { - fgMorphTreeDone(cast); - } + cast->SetMorphed(this); + fgMorphTreeDone(cast); } return cast; @@ -14680,14 +14672,7 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree) DISPTREE(tree); JITDUMP("Transformed into:\n"); DISPTREE(op); - - if (fgGlobalMorph) - { - // We can sometimes produce a comma over the constant if the original op - // had a side effect, so just ensure we set the flag (which will be already - // set for the operands otherwise). - INDEBUG(op->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); - } + op->SetMorphed(this); return op; } @@ -14742,10 +14727,9 @@ GenTree* Compiler::gtFoldExprSpecialFloating(GenTree* tree) // Helper function that creates a new IntCon node and morphs it, if required auto NewMorphedIntConNode = [&](int value) -> GenTreeIntCon* { GenTreeIntCon* icon = gtNewIconNode(value); - if (fgGlobalMorph) - { - INDEBUG(icon->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); - } + + icon->SetMorphed(this); + return icon; }; @@ -14907,14 +14891,8 @@ GenTree* Compiler::gtFoldExprSpecialFloating(GenTree* tree) DISPTREE(tree); JITDUMP("Transformed into:\n"); DISPTREE(op); + op->SetMorphed(this); - if (fgGlobalMorph) - { - // We can sometimes produce a comma over the constant if the original op - // had a side effect, so just ensure we set the flag (which will be already - // set for the operands otherwise). - INDEBUG(op->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); - } return op; } @@ -15435,6 +15413,11 @@ GenTree* Compiler::gtOptimizeEnumHasFlag(GenTree* thisOp, GenTree* flagOp) Statement* thisStoreStmt = thisOp->AsBox()->gtCopyStmtWhenInlinedBoxValue; thisStoreStmt->SetRootNode(thisStore); thisValOpt = gtNewLclvNode(thisTmp, type); + + // If this is invoked during global morph we are adding code to a remote tree + // Despite this being a store, we can't meaningfully add assertions + // + thisStore->SetMorphed(this); } if (flagVal->IsIntegralConst()) @@ -15452,6 +15435,11 @@ GenTree* Compiler::gtOptimizeEnumHasFlag(GenTree* thisOp, GenTree* flagOp) flagStoreStmt->SetRootNode(flagStore); flagValOpt = gtNewLclvNode(flagTmp, type); flagValOptCopy = gtNewLclvNode(flagTmp, type); + + // If this is invoked during global morph we are adding code to a remote tree + // Despite this being a store, we can't meaningfully add assertions + // + flagStore->SetMorphed(this); } // Turn the call into (thisValTmp & flagTmp) == flagTmp. @@ -17436,16 +17424,7 @@ void Compiler::gtExtractSideEffList(GenTree* expr, } GenTree* comma = m_compiler->gtNewOperNode(GT_COMMA, TYP_VOID, m_result, node); - -#ifdef DEBUG - if (m_compiler->fgGlobalMorph) - { - // Either both should be morphed or neither should be. - assert((m_result->gtDebugFlags & GTF_DEBUG_NODE_MORPHED) == - (node->gtDebugFlags & GTF_DEBUG_NODE_MORPHED)); - comma->gtDebugFlags |= node->gtDebugFlags & GTF_DEBUG_NODE_MORPHED; - } -#endif + comma->SetMorphed(m_compiler); // Both should have valuenumbers defined for both or for neither // one (unless we are remorphing, in which case a prior transform @@ -30762,12 +30741,7 @@ GenTree* Compiler::gtFoldExprHWIntrinsic(GenTreeHWIntrinsic* tree) } vecCon->gtSimdVal = simdVal; - - if (fgGlobalMorph) - { - INDEBUG(vecCon->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); - } - + vecCon->SetMorphed(this); fgUpdateConstTreeValueNumber(vecCon); return vecCon; } @@ -30829,11 +30803,7 @@ GenTree* Compiler::gtFoldExprHWIntrinsic(GenTreeHWIntrinsic* tree) #endif // !TARGET_XARCH && !TARGET_ARM64 DEBUG_DESTROY_NODE(op, tree); - - if (fgGlobalMorph) - { - INDEBUG(vectorNode->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); - } + vectorNode->SetMorphed(this); return vectorNode; } } @@ -32018,17 +31988,10 @@ GenTree* Compiler::gtFoldExprHWIntrinsic(GenTreeHWIntrinsic* tree) if (resultNode != tree) { - if (fgGlobalMorph) + resultNode->SetMorphed(this); + if (resultNode->OperIs(GT_COMMA)) { - // We can sometimes produce a comma over the constant if the original op - // had a side effect or even a new constant node, so just ensure we set - // the flag (which will be already set for the operands otherwise). - INDEBUG(resultNode->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); - - if (resultNode->OperIs(GT_COMMA)) - { - INDEBUG(resultNode->AsOp()->gtGetOp2()->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); - } + resultNode->AsOp()->gtGetOp2()->SetMorphed(this); } if (resultNode->OperIsConst()) @@ -32155,3 +32118,65 @@ bool Compiler::gtCanSkipCovariantStoreCheck(GenTree* value, GenTree* array) return false; } + +#if defined(DEBUG) +//------------------------------------------------------------------------ +// SetMorphed: mark a node as having been morphed +// +// Arguments: +// compiler - compiler instance +// doChildren - recursive mark child nodes +// +// Notes: +// Does nothing outside of global morph. +// +// Useful for morph post-order expansions / optimizations. +// +// Use care when invoking this on an assignment (or when doChildren is true, +// on trees containing assignments) as those usually will also require +// local assertion updates. +// +void GenTree::SetMorphed(Compiler* compiler, bool doChildren /* = false */) +{ + if (!compiler->fgGlobalMorph) + { + return; + } + + struct Visitor : GenTreeVisitor + { + enum + { + DoPostOrder = true, + }; + + Visitor(Compiler* comp) + : GenTreeVisitor(comp) + { + } + + fgWalkResult PostOrderVisit(GenTree** use, GenTree* user) + { + GenTree* const node = *use; + if (!node->WasMorphed()) + { + node->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED; + node->gtMorphCount++; + } + return Compiler::WALK_CONTINUE; + } + }; + + if (doChildren) + { + Visitor v(compiler); + GenTree* node = this; + v.WalkTree(&node, nullptr); + } + else if (!WasMorphed()) + { + gtDebugFlags |= GTF_DEBUG_NODE_MORPHED; + gtMorphCount++; + } +} +#endif diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index c0e147ee1ee95..d5b7b6066afe9 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -602,21 +602,21 @@ inline GenTreeFlags& operator ^=(GenTreeFlags& a, GenTreeFlags b) //------------------------------------------------------------------------ // GenTreeDebugFlags: a bitmask of debug-only flags for GenTree stored in gtDebugFlags // -enum GenTreeDebugFlags : unsigned int +enum GenTreeDebugFlags : unsigned short { - GTF_DEBUG_NONE = 0x00000000, // No debug flags. + GTF_DEBUG_NONE = 0x0000, // No debug flags. - GTF_DEBUG_NODE_MORPHED = 0x00000001, // the node has been morphed (in the global morphing phase) - GTF_DEBUG_NODE_SMALL = 0x00000002, - GTF_DEBUG_NODE_LARGE = 0x00000004, - GTF_DEBUG_NODE_CG_PRODUCED = 0x00000008, // genProduceReg has been called on this node - GTF_DEBUG_NODE_CG_CONSUMED = 0x00000010, // genConsumeReg has been called on this node - GTF_DEBUG_NODE_LSRA_ADDED = 0x00000020, // This node was added by LSRA + GTF_DEBUG_NODE_MORPHED = 0x0001, // the node has been morphed (in the global morphing phase) + GTF_DEBUG_NODE_SMALL = 0x0002, + GTF_DEBUG_NODE_LARGE = 0x0004, + GTF_DEBUG_NODE_CG_PRODUCED = 0x0008, // genProduceReg has been called on this node + GTF_DEBUG_NODE_CG_CONSUMED = 0x0010, // genConsumeReg has been called on this node + GTF_DEBUG_NODE_LSRA_ADDED = 0x0020, // This node was added by LSRA - GTF_DEBUG_NODE_MASK = 0x0000003F, // These flags are all node (rather than operation) properties. + GTF_DEBUG_NODE_MASK = 0x003F, // These flags are all node (rather than operation) properties. - GTF_DEBUG_VAR_CSE_REF = 0x00800000, // GT_LCL_VAR -- This is a CSE LCL_VAR node - GTF_DEBUG_CAST_DONT_FOLD = 0x00400000, // GT_CAST -- Try to prevent this cast from being folded + GTF_DEBUG_VAR_CSE_REF = 0x8000, // GT_LCL_VAR -- This is a CSE LCL_VAR node + GTF_DEBUG_CAST_DONT_FOLD = 0x4000, // GT_CAST -- Try to prevent this cast from being folded }; inline constexpr GenTreeDebugFlags operator ~(GenTreeDebugFlags a) @@ -972,7 +972,26 @@ struct GenTree #if defined(DEBUG) GenTreeDebugFlags gtDebugFlags; -#endif // defined(DEBUG) + unsigned short gtMorphCount; + void SetMorphed(Compiler* compiler, bool doChilren = false); + + bool WasMorphed() const + { + return (gtDebugFlags & GTF_DEBUG_NODE_MORPHED) != 0; + } + + void ClearMorphed() + { + gtDebugFlags &= ~GTF_DEBUG_NODE_MORPHED; + } +#else + void SetMorphed(Compiler* compiler, bool doChildren = false) + { + } + void ClearMorphed() + { + } +#endif ValueNumPair gtVNPair; diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index de3deec965c5c..7f6cd2cb3af97 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -248,6 +248,8 @@ GenTree* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, bool morphAr tree = fgMorphArgs(call); } + tree->SetMorphed(this); + return tree; } @@ -552,12 +554,12 @@ GenTree* Compiler::fgMorphExpandCast(GenTreeCast* tree) // Fix the return type to be TYP_DOUBLE // oper->gtType = TYP_DOUBLE; + oper->SetMorphed(this); // Add a Cast to TYP_FLOAT // tree = gtNewCastNode(TYP_FLOAT, oper, false, TYP_FLOAT); - INDEBUG(tree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); - + tree->SetMorphed(this); return tree; } else @@ -652,11 +654,6 @@ GenTree* Compiler::fgMorphExpandCast(GenTreeCast* tree) shiftAmount = gtFoldExpr(shiftAmount); oper->AsOp()->gtOp2 = shiftAmount; -#if DEBUG - // We may remorph the shift amount tree again later, so clear any morphed flag. - shiftAmount->gtDebugFlags &= ~GTF_DEBUG_NODE_MORPHED; -#endif // DEBUG - if (shiftAmount->IsIntegralConst()) { const ssize_t shiftAmountValue = shiftAmount->AsIntCon()->IconValue(); @@ -1660,8 +1657,7 @@ void CallArgs::EvalArgsToTemps(Compiler* comp, GenTreeCall* call) { unsigned tmpVarNum = comp->lvaGrabTemp(true DEBUGARG("argument with side effect")); GenTree* store = comp->gtNewTempStore(tmpVarNum, use.GetNode()); - - INDEBUG(store->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + store->SetMorphed(comp); if (setupArg == nullptr) { @@ -1670,10 +1666,12 @@ void CallArgs::EvalArgsToTemps(Compiler* comp, GenTreeCall* call) else { setupArg = comp->gtNewOperNode(GT_COMMA, TYP_VOID, setupArg, store); - INDEBUG(setupArg->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + setupArg->SetMorphed(comp); } - use.SetNode(comp->gtNewLclvNode(tmpVarNum, genActualType(use.GetNode()))); + GenTree* setupUse = comp->gtNewLclvNode(tmpVarNum, genActualType(use.GetNode())); + setupUse->SetMorphed(comp); + use.SetNode(setupUse); fieldList->AddAllEffectsFlags(use.GetNode()); } @@ -1685,8 +1683,7 @@ void CallArgs::EvalArgsToTemps(Compiler* comp, GenTreeCall* call) unsigned tmpVarNum = comp->lvaGrabTemp(true DEBUGARG("argument with side effect")); setupArg = comp->gtNewTempStore(tmpVarNum, argx); - - INDEBUG(setupArg->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + setupArg->SetMorphed(comp, /* doChildren */ true); LclVarDsc* varDsc = comp->lvaGetDesc(tmpVarNum); var_types lclVarType = genActualType(argx->gtType); @@ -1715,6 +1712,7 @@ void CallArgs::EvalArgsToTemps(Compiler* comp, GenTreeCall* call) // Create a copy of the temp to go to the late argument list defArg = comp->gtNewLclvNode(tmpVarNum, lclVarType); } + defArg->SetMorphed(comp); } #ifdef DEBUG @@ -1969,6 +1967,8 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call GenTree* offsetNode = comp->gtNewIconNode(comp->eeGetEEInfo()->offsetOfWrapperDelegateIndirectCell, TYP_I_IMPL); GenTree* newArg = comp->gtNewOperNode(GT_ADD, TYP_BYREF, cloned, offsetNode); + newArg->SetMorphed(comp, /* doChildren */ true); + // Append newArg as the last arg PushBack(comp, NewCallArg::Primitive(newArg).WellKnown(WellKnownArg::WrapperDelegateCell)); } @@ -3081,6 +3081,7 @@ GenTree* Compiler::fgMorphMultiregStructArg(CallArg* arg) { GenTreeLclFld* lclFld = gtNewLclFldNode(argNode->AsLclVarCommon()->GetLclNum(), genActualType(type), argNode->AsLclVarCommon()->GetLclOffs() + offset); + lclFld->SetMorphed(this); return lclFld; } else @@ -3103,11 +3104,13 @@ GenTree* Compiler::fgMorphMultiregStructArg(CallArg* arg) } GenTree* indir = gtNewIndir(type, addr); + indir->SetMorphed(this, /* doChildren*/ true); return indir; } }; newArg = new (this, GT_FIELD_LIST) GenTreeFieldList(); + newArg->SetMorphed(this); for (const ABIPassingSegment& seg : arg->NewAbiInfo.Segments()) { @@ -3172,9 +3175,11 @@ GenTreeFieldList* Compiler::fgMorphLclArgToFieldlist(GenTreeLclVarCommon* lcl) { LclVarDsc* fieldVarDsc = lvaGetDesc(fieldLclNum); GenTree* lclVar = gtNewLclvNode(fieldLclNum, fieldVarDsc->TypeGet()); + lclVar->SetMorphed(this); fieldList->AddField(this, lclVar, fieldVarDsc->lvFldOffset, fieldVarDsc->TypeGet()); fieldLclNum++; } + fieldList->SetMorphed(this); return fieldList; } @@ -3343,16 +3348,20 @@ void Compiler::fgMakeOutgoingStructArgCopy(GenTreeCall* call, CallArg* arg) // For fixed out args we create the setup node here; EvalArgsToTemps knows // to handle the case of "already have a setup node" properly. arg->SetEarlyNode(copyBlk); - arg->SetLateNode(call->gtArgs.MakeTmpArgNode(this, arg, tmp)); + GenTree* argNode = call->gtArgs.MakeTmpArgNode(this, arg, tmp); + argNode->SetMorphed(this); + arg->SetLateNode(argNode); #else // !FEATURE_FIXED_OUT_ARGS // Structs are always on the stack, and thus never need temps // so we have to put the copy and temp all into one expression. GenTree* argNode = call->gtArgs.MakeTmpArgNode(this, arg, tmp); + argNode->SetMorphed(this); // Change the expression to "(tmp=val),tmp" argNode = gtNewOperNode(GT_COMMA, argNode->TypeGet(), copyBlk, argNode); + argNode->SetMorphed(this); arg->SetEarlyNode(argNode); @@ -3789,7 +3798,6 @@ GenTree* Compiler::fgMorphIndexAddr(GenTreeIndexAddr* indexAddr) DISPTREE(tree) tree = fgMorphTree(tree); - DBEXEC(tree == indexAddr, tree->gtDebugFlags &= ~GTF_DEBUG_NODE_MORPHED); JITDUMP("fgMorphIndexAddr (after remorph):\n") DISPTREE(tree) @@ -3814,7 +3822,6 @@ GenTree* Compiler::fgMorphLeafLocal(GenTreeLclVarCommon* lclNode) if (expandedTree != nullptr) { expandedTree = fgMorphTree(expandedTree); - DBEXEC(expandedTree == lclNode, expandedTree->gtDebugFlags &= ~GTF_DEBUG_NODE_MORPHED); return expandedTree; } @@ -4192,7 +4199,7 @@ GenTree* Compiler::fgMorphFieldAddr(GenTree* tree, MorphAddrContext* mac) if (tree->OperIsSimple()) { result = fgMorphSmpOp(tree, mac); - DBEXEC(result != fieldNode, result->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + result->SetMorphed(this); // Quirk: preserve previous behavior with this NO_CSE. if (isAddr && result->OperIs(GT_COMMA)) @@ -4203,7 +4210,6 @@ GenTree* Compiler::fgMorphFieldAddr(GenTree* tree, MorphAddrContext* mac) else { result = fgMorphTree(tree, mac); - DBEXEC(result == fieldNode, result->gtDebugFlags &= ~GTF_DEBUG_NODE_MORPHED); } JITDUMP("\nFinal value of Compiler::fgMorphFieldAddr after morphing:\n"); @@ -5584,6 +5590,8 @@ GenTree* Compiler::fgMorphPotentialTailCall(GenTreeCall* call) if (isRootReplaced) { + call->SetMorphed(this); + // We have replaced the root node of this stmt and deleted the rest, // but we still have the deleted, dead nodes on the `fgMorph*` stack // if the root node was a store, `RET` or `CAST`. @@ -6668,8 +6676,11 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa // block won't be in the loop (it's assumed to have no predecessors), we need to update the special local here. if (!info.compIsStatic && (lvaArg0Var != info.compThisArg)) { - GenTree* arg0Store = gtNewStoreLclVarNode(lvaArg0Var, gtNewLclVarNode(info.compThisArg)); - Statement* arg0StoreStmt = gtNewStmt(arg0Store, callDI); + GenTree* const thisArg = gtNewLclVarNode(info.compThisArg); + thisArg->SetMorphed(this); + GenTree* const arg0Store = gtNewStoreLclVarNode(lvaArg0Var, thisArg); + arg0Store->SetMorphed(this); + Statement* const arg0StoreStmt = gtNewStmt(arg0Store, callDI); fgInsertStmtBefore(block, paramAssignmentInsertionPoint, arg0StoreStmt); } @@ -6722,8 +6733,13 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa if ((info.compInitMem && (isUserLocal || structWithGCFields)) || hadSuppressedInit) { GenTree* zero = (lclType == TYP_STRUCT) ? gtNewIconNode(0) : gtNewZeroConNode(lclType); + zero->SetMorphed(this); GenTree* init = gtNewStoreLclVarNode(varNum, zero); - init->gtType = lclType; // TODO-ASG: delete this zero-diff quirk. + + // No need for assertion prop here since the first block is now an (opaque) join + // and has already been morphed. + init->SetMorphed(this); + init->gtType = lclType; // TODO-ASG: delete this zero-diff quirk. if (lclType == TYP_STRUCT) { init = fgMorphInitBlock(init); @@ -6828,17 +6844,27 @@ Statement* Compiler::fgAssignRecursiveCallArgToCallerParam(GenTree* arg, // The argument is not assigned to a temp. We need to create a new temp and insert a store. unsigned tmpNum = lvaGrabTemp(true DEBUGARG("arg temp")); lvaTable[tmpNum].lvType = arg->gtType; - GenTree* tempSrc = arg; - GenTree* tmpStoreNode = gtNewStoreLclVarNode(tmpNum, tempSrc); + GenTree* tempSrc = arg; + GenTree* tmpStoreNode = gtNewStoreLclVarNode(tmpNum, tempSrc); + tmpStoreNode->SetMorphed(this); Statement* tmpStoreStmt = gtNewStmt(tmpStoreNode, callDI); fgInsertStmtBefore(block, tmpAssignmentInsertionPoint, tmpStoreStmt); argInTemp = gtNewLclvNode(tmpNum, tempSrc->gtType); + + // No need for assertion prop here since the first block is now an opqaque join + // and has laready been morphed + argInTemp->SetMorphed(this); } // Now assign the temp to the parameter. assert(lvaGetDesc(lclParamNum)->lvIsParam); GenTree* paramStoreNode = gtNewStoreLclVarNode(lclParamNum, argInTemp); - paramAssignStmt = gtNewStmt(paramStoreNode, callDI); + + // No need for assertion prop here since the first block is now an opqaque join + // and has laready been morphed + paramStoreNode->SetMorphed(this); + + paramAssignStmt = gtNewStmt(paramStoreNode, callDI); fgInsertStmtBefore(block, paramAssignmentInsertionPoint, paramAssignStmt); } @@ -6899,7 +6925,7 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call) JITDUMP("\nInserting store of a multi-reg call result to a temp:\n"); DISPSTMT(storeStmt); - INDEBUG(result->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + result->SetMorphed(this); return result; } @@ -7091,32 +7117,18 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call) else { argSetup = new (this, GT_COMMA) GenTreeOp(GT_COMMA, TYP_VOID, argSetup, setupArgNode); -#if DEBUG - argSetup->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED; -#endif // DEBUG + argSetup->SetMorphed(this); } } -#ifdef DEBUG - auto resetMorphedFlag = [](GenTree** slot, fgWalkData* data) -> fgWalkResult { - (*slot)->gtDebugFlags &= ~GTF_DEBUG_NODE_MORPHED; - return WALK_CONTINUE; - }; - - fgWalkTreePost(&arr, resetMorphedFlag); - fgWalkTreePost(&index, resetMorphedFlag); - fgWalkTreePost(&value, resetMorphedFlag); -#endif // DEBUG - GenTree* indexAddr = gtNewArrayIndexAddr(arr, index, TYP_REF, NO_CLASS_HANDLE); GenTree* store = gtNewStoreIndNode(TYP_REF, indexAddr, value); GenTree* result = fgMorphTree(store); + if (argSetup != nullptr) { result = new (this, GT_COMMA) GenTreeOp(GT_COMMA, TYP_VOID, argSetup, result); -#if DEBUG - result->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED; -#endif // DEBUG + result->SetMorphed(this); } return result; @@ -7568,7 +7580,7 @@ GenTreeOp* Compiler::fgMorphCommutative(GenTreeOp* tree) DEBUG_DESTROY_NODE(tree); DEBUG_DESTROY_NODE(cns2); DEBUG_DESTROY_NODE(foldedCns); - INDEBUG(cns1->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + cns1->SetMorphed(this); return op1->AsOp(); } @@ -7635,7 +7647,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA GenTree* expandedTree = fgMorphExpandLocal(tree->AsLclVarCommon()); if (expandedTree != nullptr) { - DBEXEC(tree != expandedTree, expandedTree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + expandedTree->SetMorphed(this); tree = expandedTree; oper = tree->OperGet(); op1 = tree->gtGetOp1(); @@ -7739,7 +7751,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA GenTreeIntCon* iconNode = gtNewStringLiteralLength(op1->AsStrCon()); if (iconNode != nullptr) { - INDEBUG(iconNode->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + iconNode->SetMorphed(this); return iconNode; } } @@ -7752,7 +7764,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA if (constNode != nullptr) { assert(constNode->OperIsConst()); // No further morphing required. - INDEBUG(constNode->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + constNode->SetMorphed(this); return constNode; } } @@ -7884,6 +7896,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA if (op2->OperIs(GT_CNS_NATIVELONG) && op2->AsIntConCommon()->LngValue() >= 2 && op2->AsIntConCommon()->LngValue() <= 0x3fffffff) { + op2->SetMorphed(this); tree->AsOp()->gtOp1 = op1 = fgMorphTree(op1); noway_assert(op1->TypeIs(TYP_LONG)); @@ -8681,6 +8694,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA { GenTree* newOp1 = op1op1; // a GenTree* newOp2 = gtNewIconNodeWithVN(this, -constVal, op1op2->TypeGet()); // -C + newOp2->SetMorphed(this); mulOrDiv->gtOp1 = newOp1; mulOrDiv->gtOp2 = newOp2; mulOrDiv->SetVNsFromNode(tree); @@ -8741,9 +8755,8 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA commaNode->gtFlags = (treeFlags & ~GTF_REVERSE_OPS); // Bashing the GT_COMMA flags here is // dangerous, clear the GTF_REVERSE_OPS at // least. -#ifdef DEBUG - commaNode->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED; -#endif + commaNode->SetMorphed(this); + while (commaNode->AsOp()->gtOp2->gtOper == GT_COMMA) { commaNode = commaNode->AsOp()->gtOp2; @@ -8754,9 +8767,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA // least. commaNode->gtFlags |= ((commaNode->AsOp()->gtOp1->gtFlags | commaNode->AsOp()->gtOp2->gtFlags) & (GTF_ASG | GTF_CALL)); -#ifdef DEBUG - commaNode->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED; -#endif + commaNode->SetMorphed(this); } tree = op1; @@ -8781,10 +8792,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA } op1->gtFlags |= treeFlags & GTF_GLOB_REF; - -#ifdef DEBUG - op1->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED; -#endif + op1->SetMorphed(this); commaNode->AsOp()->gtOp2 = op1; commaNode->gtFlags |= (op1->gtFlags & GTF_ALL_EFFECT); return tree; @@ -8799,7 +8807,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA if ((op1->gtFlags & GTF_SIDE_EFFECT) != 0) { tree = gtUnusedValNode(op1); - INDEBUG(tree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + tree->SetMorphed(this, /* doChildren */ true); } else { @@ -8917,7 +8925,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA JITDUMP("false\n"); tree = gtWrapWithSideEffects(gtNewIconNode(0), op1, GTF_ALL_EFFECT); } - INDEBUG(tree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + tree->SetMorphed(this); return tree; } break; @@ -9524,7 +9532,9 @@ GenTree* Compiler::fgOptimizeEqualityComparisonWithConst(GenTreeOp* cmp) } else { - andOp->gtOp1 = gtNewCastNode(TYP_INT, andOp->gtGetOp1(), false, TYP_INT); + GenTree* const newOp1 = gtNewCastNode(TYP_INT, andOp->gtGetOp1(), false, TYP_INT); + newOp1->SetMorphed(this); + andOp->gtOp1 = newOp1; } assert(andMask == andOp->gtGetOp2()); @@ -9651,8 +9661,7 @@ GenTree* Compiler::fgOptimizeRelationalComparisonWithFullRangeConst(GenTreeOp* c fgUpdateConstTreeValueNumber(ret); DEBUG_DESTROY_NODE(cmp); - - INDEBUG(ret->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + ret->SetMorphed(this); return ret; } @@ -9784,12 +9793,12 @@ GenTree* Compiler::fgOptimizeHWIntrinsic(GenTreeHWIntrinsic* node) if (optimizedTree != node) { assert(!fgIsCommaThrow(optimizedTree)); - INDEBUG(optimizedTree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + optimizedTree->SetMorphed(this); return optimizedTree; } else if (!optimizedTree->OperIsHWIntrinsic()) { - INDEBUG(optimizedTree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + optimizedTree->SetMorphed(this); return optimizedTree; } } @@ -9911,8 +9920,7 @@ GenTree* Compiler::fgOptimizeHWIntrinsic(GenTreeHWIntrinsic* node) { node = hwop1; } - - INDEBUG(node->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + node->SetMorphed(this); return node; } @@ -10060,9 +10068,9 @@ GenTree* Compiler::fgOptimizeHWIntrinsic(GenTreeHWIntrinsic* node) DEBUG_DESTROY_NODE(actualOp2); } + node->SetMorphed(this); node = gtNewSimdCvtMaskToVectorNode(retType, node, simdBaseJitType, simdSize)->AsHWIntrinsic(); - - INDEBUG(node->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + node->SetMorphed(this); return node; } #endif // FEATURE_MASKED_HW_INTRINSICS @@ -10162,7 +10170,7 @@ GenTree* Compiler::fgOptimizeHWIntrinsic(GenTreeHWIntrinsic* node) DEBUG_DESTROY_NODE(op1); DEBUG_DESTROY_NODE(node); - INDEBUG(negNode->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + negNode->SetMorphed(this); return negNode; #elif defined(TARGET_XARCH) @@ -10219,7 +10227,7 @@ GenTree* Compiler::fgOptimizeHWIntrinsic(GenTreeHWIntrinsic* node) DEBUG_DESTROY_NODE(op2); DEBUG_DESTROY_NODE(node); - INDEBUG(notNode->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + notNode->SetMorphed(this); return notNode; } @@ -10231,7 +10239,7 @@ GenTree* Compiler::fgOptimizeHWIntrinsic(GenTreeHWIntrinsic* node) DEBUG_DESTROY_NODE(op2); DEBUG_DESTROY_NODE(node); - INDEBUG(negNode->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + negNode->SetMorphed(this); return negNode; } @@ -10622,8 +10630,7 @@ GenTree* Compiler::fgOptimizeMultiply(GenTreeOp* mul) { op2 = fgMakeMultiUse(&op1); GenTree* add = gtNewOperNode(GT_ADD, mul->TypeGet(), op1, op2); - INDEBUG(add->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); - + add->SetMorphed(this, /* doChildren */ true); return add; } } @@ -10712,7 +10719,9 @@ GenTree* Compiler::fgOptimizeMultiply(GenTreeOp* mul) } // change the multiplication into a smaller multiplication (by 3, 5 or 9) and a shift - op1 = gtNewOperNode(GT_MUL, mul->TypeGet(), op1, gtNewIconNodeWithVN(this, factor, mul->TypeGet())); + GenTree* const factorNode = gtNewIconNodeWithVN(this, factor, mul->TypeGet()); + factorNode->SetMorphed(this); + op1 = gtNewOperNode(GT_MUL, mul->TypeGet(), op1, factorNode); mul->gtOp1 = op1; fgMorphTreeDone(op1); @@ -11465,7 +11474,7 @@ GenTree* Compiler::fgMorphHWIntrinsic(GenTreeHWIntrinsic* tree) if ((morphedTree != tree) || !morphedTree->OperIsHWIntrinsic()) { - INDEBUG(morphedTree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); + morphedTree->SetMorphed(this); } else { @@ -11534,19 +11543,15 @@ GenTree* Compiler::fgMorphModToZero(GenTreeOp* tree) if (op1SideEffects != nullptr) { GenTree* comma = gtNewOperNode(GT_COMMA, zero->TypeGet(), op1SideEffects, zero); - INDEBUG(comma->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); - + comma->SetMorphed(this); DEBUG_DESTROY_NODE(tree); - return comma; } else { - INDEBUG(zero->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); - + zero->SetMorphed(this); DEBUG_DESTROY_NODE(tree->gtOp1); DEBUG_DESTROY_NODE(tree); - return zero; } } @@ -11672,14 +11677,9 @@ GenTree* Compiler::fgMorphModToSubMulDiv(GenTreeOp* tree) result = gtNewOperNode(GT_COMMA, type, tempInfos[i].store, result); } -#ifdef DEBUG - result->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED; -#endif - + result->SetMorphed(this); optRecordSsaUses(result, compCurBB); - div->CheckDivideByConstOptimized(this); - return result; } @@ -11710,11 +11710,9 @@ GenTree* Compiler::fgMorphUModToAndSub(GenTreeOp* tree) const size_t cnsValue = (static_cast(tree->gtOp2->AsIntConCommon()->IntegralValue())) - 1; GenTree* const newTree = gtNewOperNode(GT_AND, type, tree->gtOp1, gtNewIconNodeWithVN(this, cnsValue, type)); - INDEBUG(newTree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); - + newTree->SetMorphed(this); DEBUG_DESTROY_NODE(tree->gtOp2); DEBUG_DESTROY_NODE(tree); - return newTree; } @@ -12053,7 +12051,9 @@ GenTreeOp* Compiler::fgMorphLongMul(GenTreeOp* mul) mul->SetAllEffectsFlags(op1, op2); op1->SetDoNotCSE(); + op1->SetMorphed(this); op2->SetDoNotCSE(); + op2->SetMorphed(this); return mul; } @@ -12067,6 +12067,7 @@ GenTreeOp* Compiler::fgMorphLongMul(GenTreeOp* mul) GenTree* Compiler::fgMorphTree(GenTree* tree, MorphAddrContext* mac) { assert(tree); + tree->ClearMorphed(); #ifdef DEBUG if (verbose) @@ -12129,9 +12130,6 @@ GenTree* Compiler::fgMorphTree(GenTree* tree, MorphAddrContext* mac) if (fgGlobalMorph) { - /* Ensure that we haven't morphed this node already */ - assert(((tree->gtDebugFlags & GTF_DEBUG_NODE_MORPHED) == 0) && "ERROR: Already morphed this node!"); - /* Before morphing the tree, we try to propagate any active assertions */ if (optLocalAssertionProp) { @@ -12152,10 +12150,6 @@ GenTree* Compiler::fgMorphTree(GenTree* tree, MorphAddrContext* mac) PREFAST_ASSUME(tree != nullptr); } - /* Save the original un-morphed tree for fgMorphTreeDone */ - - GenTree* const oldTree = tree; - /* Figure out what kind of a node we have */ unsigned const kind = tree->OperKind(); @@ -12279,8 +12273,7 @@ GenTree* Compiler::fgMorphTree(GenTree* tree, MorphAddrContext* mac) } DONE: - const bool isNewTree = (oldTree != tree); - fgMorphTreeDone(tree, optAssertionPropDone, isNewTree DEBUGARG(thisMorphNum)); + fgMorphTreeDone(tree, optAssertionPropDone DEBUGARG(thisMorphNum)); return tree; } @@ -12527,12 +12520,11 @@ void Compiler::fgAssertionGen(GenTree* tree) // tree - the tree after morphing // // Notes: -// Simple version where the tree has not been marked -// as morphed, and where assertion kill/gen has not yet been done. +// Simple version where assertion kill/gen has not yet been done. // void Compiler::fgMorphTreeDone(GenTree* tree) { - fgMorphTreeDone(tree, false, false); + fgMorphTreeDone(tree, false); } //------------------------------------------------------------------------ @@ -12545,15 +12537,13 @@ void Compiler::fgMorphTreeDone(GenTree* tree) // morphNum - counts invocations of fgMorphTree // // Notes: -// This function is called to complete the morphing of a tree node +// This function is called to complete the morphing of a tree node. // It should only be called once for each node. -// If DEBUG is defined the flag GTF_DEBUG_NODE_MORPHED is checked and updated, -// to enforce the invariant that each node is only morphed once. // // When local assertion prop is active assertions are killed and generated // based on tree (unless optAssertionPropDone is true). // -void Compiler::fgMorphTreeDone(GenTree* tree, bool optAssertionPropDone, bool isMorphedTree DEBUGARG(int morphNum)) +void Compiler::fgMorphTreeDone(GenTree* tree, bool optAssertionPropDone DEBUGARG(int morphNum)) { #ifdef DEBUG if (verbose && treesBeforeAfterMorph) @@ -12569,19 +12559,7 @@ void Compiler::fgMorphTreeDone(GenTree* tree, bool optAssertionPropDone, bool is return; } - if (isMorphedTree) - { - // caller should have set the morphed flag - // - assert((tree->gtDebugFlags & GTF_DEBUG_NODE_MORPHED) && "ERROR: Did not morph this node!"); - } - else - { - // caller should not have set the morphed flag - // - assert(((tree->gtDebugFlags & GTF_DEBUG_NODE_MORPHED) == 0) && "ERROR: Already morphed this node!"); - INDEBUG(tree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); - } + tree->SetMorphed(this); // Note "tree" may generate new assertions that we // miss if we did them early... perhaps we should skip @@ -13080,16 +13058,6 @@ void Compiler::fgMorphStmts(BasicBlock* block) morphedTree = stmt->GetRootNode(); } - else - { - /* This must be a tailcall that caused a GCPoll to get - injected. We haven't actually morphed the call yet - but the flag still got set, clear it here... */ - -#ifdef DEBUG - morphedTree->gtDebugFlags &= ~GTF_DEBUG_NODE_MORPHED; -#endif - } noway_assert(compTailCallUsed); noway_assert(morphedTree->gtOper == GT_CALL); @@ -13120,6 +13088,7 @@ void Compiler::fgMorphStmts(BasicBlock* block) morphedTree = gtCloneExpr(morphedTree); noway_assert(morphedTree != nullptr); + morphedTree->SetMorphed(this, /* doChildren*/ true); if (verbose) { @@ -13615,9 +13584,51 @@ PhaseStatus Compiler::fgMorphBlocks() // may no longer be canonical. fgCanonicalizeFirstBB(); + INDEBUG(fgPostGlobalMorphChecks();) + return PhaseStatus::MODIFIED_EVERYTHING; } +#ifdef DEBUG + +//------------------------------------------------------------------------ +// fgPostGlobalMorphChecks: Make sure all nodes were morphed +// +void Compiler::fgPostGlobalMorphChecks() +{ + struct Visitor : GenTreeVisitor + { + enum + { + DoPostOrder = true, + }; + + Visitor(Compiler* comp) + : GenTreeVisitor(comp) + { + } + + fgWalkResult PostOrderVisit(GenTree** use, GenTree* user) + { + assert((*use)->WasMorphed()); + assert((*use)->gtMorphCount <= 5); + return WALK_CONTINUE; + } + }; + + Visitor v(this); + + for (BasicBlock* const block : Blocks()) + { + for (Statement* const stmt : block->Statements()) + { + v.WalkTree(stmt->GetRootNodePointer(), nullptr); + } + } +} + +#endif + //------------------------------------------------------------------------ // fgGetFirstILBB: Obtain the first basic block that was created due to IL. // @@ -13807,7 +13818,8 @@ void Compiler::fgMergeBlockReturn(BasicBlock* block) assert(genReturnErrorLocal != BAD_VAR_NUM); const DebugInfo& di = lastStmt->GetDebugInfo(); GenTree* swiftErrorStore = gtNewTempStore(genReturnErrorLocal, ret->gtGetOp1()); - Statement* const newStmt = gtNewStmt(swiftErrorStore, di); + swiftErrorStore->SetMorphed(this); + Statement* const newStmt = gtNewStmt(swiftErrorStore, di); fgInsertStmtBefore(block, lastStmt, newStmt); } #endif // SWIFT_SUPPORT @@ -13833,6 +13845,8 @@ void Compiler::fgMergeBlockReturn(BasicBlock* block) Statement* pAfterStatement = lastStmt; const DebugInfo& di = lastStmt->GetDebugInfo(); GenTree* tree = gtNewTempStore(genReturnLocal, retVal, CHECK_SPILL_NONE, &pAfterStatement, di, block); + // TODO: assertion gen/kill? + tree->SetMorphed(this); if (tree->OperIsCopyBlkOp()) { tree = fgMorphCopyBlock(tree); diff --git a/src/coreclr/jit/morphblock.cpp b/src/coreclr/jit/morphblock.cpp index f21cc2355a486..e1f0baf084fc2 100644 --- a/src/coreclr/jit/morphblock.cpp +++ b/src/coreclr/jit/morphblock.cpp @@ -132,14 +132,7 @@ GenTree* MorphInitBlockHelper::Morph() assert(m_transformationDecision != BlockTransformation::Undefined); assert(m_result != nullptr); -#ifdef DEBUG - // If we are going to return a different node than the input then morph - // expects us to have set GTF_DEBUG_NODE_MORPHED. - if ((m_result != m_store) || (sideEffects != nullptr)) - { - m_result->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED; - } -#endif + m_result->SetMorphed(m_comp); while (sideEffects != nullptr) { @@ -160,8 +153,7 @@ GenTree* MorphInitBlockHelper::Morph() { m_result = m_comp->gtNewOperNode(GT_COMMA, TYP_VOID, sideEffects, m_result); } - INDEBUG(m_result->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED); - + m_result->SetMorphed(m_comp); sideEffects = sideEffects->gtNext; } @@ -414,7 +406,8 @@ void MorphInitBlockHelper::TryInitFieldByField() LclVarDsc* fieldDesc = m_comp->lvaGetDesc(fieldLclNum); var_types fieldType = fieldDesc->TypeGet(); - GenTree* src = m_comp->gtNewConWithPattern(fieldType, initPattern); + GenTree* src = m_comp->gtNewConWithPattern(fieldType, initPattern); + src->SetMorphed(m_comp); GenTree* store = m_comp->gtNewTempStore(fieldLclNum, src); if (m_comp->optLocalAssertionProp) @@ -422,9 +415,12 @@ void MorphInitBlockHelper::TryInitFieldByField() m_comp->fgAssertionGen(store); } + store->SetMorphed(m_comp); + if (tree != nullptr) { tree = m_comp->gtNewOperNode(GT_COMMA, TYP_VOID, tree, store); + tree->SetMorphed(m_comp); } else { @@ -435,6 +431,7 @@ void MorphInitBlockHelper::TryInitFieldByField() if (tree == nullptr) { tree = m_comp->gtNewNothingNode(); + tree->SetMorphed(m_comp); } m_result = tree; @@ -556,8 +553,12 @@ GenTree* MorphInitBlockHelper::EliminateCommas(GenTree** commaPool) { unsigned lhsAddrLclNum = m_comp->lvaGrabTemp(true DEBUGARG("Block morph LHS addr")); - addSideEffect(m_comp->gtNewTempStore(lhsAddrLclNum, addr)); - m_store->AsUnOp()->gtOp1 = m_comp->gtNewLclvNode(lhsAddrLclNum, genActualType(addr)); + GenTree* const tempStore = m_comp->gtNewTempStore(lhsAddrLclNum, addr); + tempStore->SetMorphed(m_comp); + addSideEffect(tempStore); + GenTree* const tempRead = m_comp->gtNewLclvNode(lhsAddrLclNum, genActualType(addr)); + tempRead->SetMorphed(m_comp); + m_store->AsUnOp()->gtOp1 = tempRead; m_comp->gtUpdateNodeSideEffects(m_store); } } @@ -1160,6 +1161,8 @@ GenTree* MorphCopyBlockHelper::CopyFieldByField() LclVarDsc* addrSpillDsc = m_comp->lvaGetDesc(addrSpillTemp); addrSpillStore = m_comp->gtNewTempStore(addrSpillTemp, addrSpill); + // TODO: assertion prop? + addrSpillStore->SetMorphed(m_comp); } auto grabAddr = [=, &result](unsigned offs) { @@ -1169,6 +1172,7 @@ GenTree* MorphCopyBlockHelper::CopyFieldByField() { assert(addrSpillTemp != BAD_VAR_NUM); addrClone = m_comp->gtNewLclvNode(addrSpillTemp, addrSpill->TypeGet()); + addrClone->SetMorphed(m_comp); } else { @@ -1201,13 +1205,15 @@ GenTree* MorphCopyBlockHelper::CopyFieldByField() // that takes field seq to get correct overflow // handling. GenTreeIntCon* fldOffsetNode = m_comp->gtNewIconNode(fullOffs, TYP_I_IMPL); - fldOffsetNode->gtFieldSeq = addrBaseOffsFldSeq; + fldOffsetNode->SetMorphed(m_comp); + fldOffsetNode->gtFieldSeq = addrBaseOffsFldSeq; addrClone = m_comp->gtNewOperNode(GT_ADD, varTypeIsGC(addrClone) ? TYP_BYREF : TYP_I_IMPL, addrClone, fldOffsetNode); // Avoid constant prop propagating each field access with a large // constant address. TODO-Cleanup: We should tune constant prop to // have better heuristics around this. addrClone->gtFlags |= GTF_DONT_CSE; + addrClone->SetMorphed(m_comp); } return addrClone; @@ -1310,6 +1316,7 @@ GenTree* MorphCopyBlockHelper::CopyFieldByField() } } assert(srcFld != nullptr); + srcFld->SetMorphed(m_comp); GenTree* dstFldStore; if (m_dstDoFldStore) @@ -1357,6 +1364,7 @@ GenTree* MorphCopyBlockHelper::CopyFieldByField() } } noway_assert(dstFldStore->TypeGet() == srcFld->TypeGet()); + dstFldStore->SetMorphed(m_comp); if (m_comp->optLocalAssertionProp) { @@ -1367,10 +1375,12 @@ GenTree* MorphCopyBlockHelper::CopyFieldByField() { result = m_comp->gtNewOperNode(GT_COMMA, TYP_VOID, addrSpillStore, dstFldStore); addrSpillStore = nullptr; + result->SetMorphed(m_comp); } else if (result != nullptr) { result = m_comp->gtNewOperNode(GT_COMMA, TYP_VOID, result, dstFldStore); + result->SetMorphed(m_comp); } else { diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 35e58d539eea8..c734c1c8a7a3a 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -3320,9 +3320,7 @@ bool Compiler::optNarrowTree(GenTree* tree, var_types srct, var_types dstt, Valu { assert(tree->gtType == TYP_INT); GenTree* castOp = gtNewCastNode(TYP_INT, *otherOpPtr, false, TYP_INT); -#ifdef DEBUG - castOp->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED; -#endif + castOp->SetMorphed(this); *otherOpPtr = castOp; } } From 43f7d321228c7e03f537d95b7193845b89b50709 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 18 Dec 2024 15:23:09 -0800 Subject: [PATCH 15/25] JIT: remove phis right after optimizing (#110821) They serve no useful purpose once SSA is gone, and they potentially confuse other parts of the JIT. Was also going to make the PHIs zero cost, but this apparently was already done (not sure when/how). Closes #88841 --- src/coreclr/jit/compiler.cpp | 5 +++- src/coreclr/jit/compiler.h | 2 +- src/coreclr/jit/ssabuilder.cpp | 52 +++++++++++++++++++++++----------- 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index a3d854e17eff0..0ed95870ea2e0 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -5006,6 +5006,9 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl // Iterate if requested, resetting annotations first. if (opts.optRepeatIteration == opts.optRepeatCount) { + // If we're done optimizing, just remove the PHIs + // + fgResetForSsa(/* deepClean */ false); break; } @@ -5849,7 +5852,7 @@ void Compiler::ResetOptAnnotations() { assert(opts.optRepeat); assert(JitConfig.JitOptRepeatCount() > 0); - fgResetForSsa(); + fgResetForSsa(/* deepClean */ true); vnStore = nullptr; m_blockToEHPreds = nullptr; m_dominancePreds = nullptr; diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index d99813fe5290f..ba1a1f34f89a4 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -5725,7 +5725,7 @@ class Compiler PhaseStatus fgSsaBuild(); // Reset any data structures to the state expected by "fgSsaBuild", so it can be run again. - void fgResetForSsa(); + void fgResetForSsa(bool deepClean); unsigned fgSsaPassesCompleted = 0; // Number of times fgSsaBuild has been run. bool fgSsaValid = false; // True if SSA info is valid and can be cross-checked versus IR diff --git a/src/coreclr/jit/ssabuilder.cpp b/src/coreclr/jit/ssabuilder.cpp index b36529cc1a686..872d1d5fa87ec 100644 --- a/src/coreclr/jit/ssabuilder.cpp +++ b/src/coreclr/jit/ssabuilder.cpp @@ -15,7 +15,7 @@ PhaseStatus Compiler::fgSsaBuild() // If this is not the first invocation, reset data structures for SSA. if (fgSsaPassesCompleted > 0) { - fgResetForSsa(); + fgResetForSsa(/* deepClean */ true); } SsaBuilder builder(this); @@ -29,21 +29,36 @@ PhaseStatus Compiler::fgSsaBuild() return PhaseStatus::MODIFIED_EVERYTHING; } -void Compiler::fgResetForSsa() +//------------------------------------------------------------------------ +// fgResetForSsa: remove SSA artifacts +// +// Arguments: +// deepClean - if true, remove all SSA artifacts +// if false, just remove PHIs +// +// Notes: +// deepCleaning is needed in order to rebuild SSA. +// +void Compiler::fgResetForSsa(bool deepClean) { - for (unsigned i = 0; i < lvaCount; ++i) - { - lvaTable[i].lvPerSsaData.Reset(); - } - lvMemoryPerSsaData.Reset(); - for (MemoryKind memoryKind : allMemoryKinds()) - { - m_memorySsaMap[memoryKind] = nullptr; - } + JITDUMP("Removing %s\n", deepClean ? "all SSA artifacts" : "PHI functions"); - if (m_outlinedCompositeSsaNums != nullptr) + if (deepClean) { - m_outlinedCompositeSsaNums->Reset(); + for (unsigned i = 0; i < lvaCount; ++i) + { + lvaTable[i].lvPerSsaData.Reset(); + } + lvMemoryPerSsaData.Reset(); + for (MemoryKind memoryKind : allMemoryKinds()) + { + m_memorySsaMap[memoryKind] = nullptr; + } + + if (m_outlinedCompositeSsaNums != nullptr) + { + m_outlinedCompositeSsaNums->Reset(); + } } for (BasicBlock* const blk : Blocks()) @@ -63,13 +78,16 @@ void Compiler::fgResetForSsa() } } - for (Statement* const stmt : blk->Statements()) + if (deepClean) { - for (GenTree* const tree : stmt->TreeList()) + for (Statement* const stmt : blk->Statements()) { - if (tree->IsAnyLocal()) + for (GenTree* const tree : stmt->TreeList()) { - tree->AsLclVarCommon()->SetSsaNum(SsaConfig::RESERVED_SSA_NUM); + if (tree->IsAnyLocal()) + { + tree->AsLclVarCommon()->SetSsaNum(SsaConfig::RESERVED_SSA_NUM); + } } } } From 54527ea96e15b85270a0c2c2fc3f54aa64377e53 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Thu, 19 Dec 2024 00:28:32 +0100 Subject: [PATCH 16/25] Fix race condition when cancelling pending HTTP connection attempts (#110744) * Fix race condition when cancelling pending HTTP connection attempts * Improve comments and naming --- .../HttpConnectionPool.Http1.cs | 3 +- .../HttpConnectionPool.Http2.cs | 3 +- .../HttpConnectionPool.Http3.cs | 75 ++++++++++--------- .../ConnectionPool/HttpConnectionPool.cs | 30 +++++++- .../ConnectionPool/HttpConnectionWaiter.cs | 2 +- .../SocketsHttpHandlerTest.Cancellation.cs | 62 +++++++++++++++ 6 files changed, 133 insertions(+), 42 deletions(-) diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionPool/HttpConnectionPool.Http1.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionPool/HttpConnectionPool.Http1.cs index d65e1b6486de2..af78d4d4cb9a9 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionPool/HttpConnectionPool.Http1.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionPool/HttpConnectionPool.Http1.cs @@ -259,8 +259,7 @@ private async Task InjectNewHttp11ConnectionAsync(RequestQueue.Q HttpConnection? connection = null; Exception? connectionException = null; - CancellationTokenSource cts = GetConnectTimeoutCancellationTokenSource(); - waiter.ConnectionCancellationTokenSource = cts; + CancellationTokenSource cts = GetConnectTimeoutCancellationTokenSource(waiter); try { connection = await CreateHttp11ConnectionAsync(queueItem.Request, true, cts.Token).ConfigureAwait(false); diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionPool/HttpConnectionPool.Http2.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionPool/HttpConnectionPool.Http2.cs index c3999c520f0f2..33155f12d8b73 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionPool/HttpConnectionPool.Http2.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionPool/HttpConnectionPool.Http2.cs @@ -181,8 +181,7 @@ private async Task InjectNewHttp2ConnectionAsync(RequestQueue. Exception? connectionException = null; HttpConnectionWaiter waiter = queueItem.Waiter; - CancellationTokenSource cts = GetConnectTimeoutCancellationTokenSource(); - waiter.ConnectionCancellationTokenSource = cts; + CancellationTokenSource cts = GetConnectTimeoutCancellationTokenSource(waiter); try { (Stream stream, TransportContext? transportContext, Activity? activity, IPEndPoint? remoteEndPoint) = await ConnectAsync(queueItem.Request, true, cts.Token).ConfigureAwait(false); diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionPool/HttpConnectionPool.Http3.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionPool/HttpConnectionPool.Http3.cs index bae0ef3895255..af6fd1e2fb373 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionPool/HttpConnectionPool.Http3.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionPool/HttpConnectionPool.Http3.cs @@ -75,52 +75,60 @@ internal sealed partial class HttpConnectionPool // Loop in case we get a 421 and need to send the request to a different authority. while (true) { - if (!TryGetHttp3Authority(request, out HttpAuthority? authority, out Exception? reasonException)) + HttpConnectionWaiter? http3ConnectionWaiter = null; + try { - if (reasonException is null) + if (!TryGetHttp3Authority(request, out HttpAuthority? authority, out Exception? reasonException)) { - return null; + if (reasonException is null) + { + return null; + } + ThrowGetVersionException(request, 3, reasonException); } - ThrowGetVersionException(request, 3, reasonException); - } - long queueStartingTimestamp = HttpTelemetry.Log.IsEnabled() || Settings._metrics!.RequestsQueueDuration.Enabled ? Stopwatch.GetTimestamp() : 0; - Activity? waitForConnectionActivity = ConnectionSetupDistributedTracing.StartWaitForConnectionActivity(authority); + long queueStartingTimestamp = HttpTelemetry.Log.IsEnabled() || Settings._metrics!.RequestsQueueDuration.Enabled ? Stopwatch.GetTimestamp() : 0; + Activity? waitForConnectionActivity = ConnectionSetupDistributedTracing.StartWaitForConnectionActivity(authority); - if (!TryGetPooledHttp3Connection(request, out Http3Connection? connection, out HttpConnectionWaiter? http3ConnectionWaiter)) - { - try + if (!TryGetPooledHttp3Connection(request, out Http3Connection? connection, out http3ConnectionWaiter)) { - connection = await http3ConnectionWaiter.WaitWithCancellationAsync(cancellationToken).ConfigureAwait(false); + try + { + connection = await http3ConnectionWaiter.WaitWithCancellationAsync(cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + ConnectionSetupDistributedTracing.ReportError(waitForConnectionActivity, ex); + waitForConnectionActivity?.Stop(); + throw; + } } - catch (Exception ex) + + // Request cannot be sent over H/3 connection, try downgrade or report failure. + // Note that if there's an H/3 suitable origin authority but is unavailable or blocked via Alt-Svc, exception is thrown instead. + if (connection is null) { - ConnectionSetupDistributedTracing.ReportError(waitForConnectionActivity, ex); - waitForConnectionActivity?.Stop(); - throw; + return null; } - } - // Request cannot be sent over H/3 connection, try downgrade or report failure. - // Note that if there's an H/3 suitable origin authority but is unavailable or blocked via Alt-Svc, exception is thrown instead. - if (connection is null) - { - return null; - } + HttpResponseMessage response = await connection.SendAsync(request, queueStartingTimestamp, waitForConnectionActivity, cancellationToken).ConfigureAwait(false); - HttpResponseMessage response = await connection.SendAsync(request, queueStartingTimestamp, waitForConnectionActivity, cancellationToken).ConfigureAwait(false); + // If an Alt-Svc authority returns 421, it means it can't actually handle the request. + // An authority is supposed to be able to handle ALL requests to the origin, so this is a server bug. + // In this case, we blocklist the authority and retry the request at the origin. + if (response.StatusCode == HttpStatusCode.MisdirectedRequest && connection.Authority != _originAuthority) + { + response.Dispose(); + BlocklistAuthority(connection.Authority); + continue; + } - // If an Alt-Svc authority returns 421, it means it can't actually handle the request. - // An authority is supposed to be able to handle ALL requests to the origin, so this is a server bug. - // In this case, we blocklist the authority and retry the request at the origin. - if (response.StatusCode == HttpStatusCode.MisdirectedRequest && connection.Authority != _originAuthority) + return response; + } + finally { - response.Dispose(); - BlocklistAuthority(connection.Authority); - continue; + http3ConnectionWaiter?.SetTimeoutToPendingConnectionAttempt(this, cancellationToken.IsCancellationRequested); } - - return response; } } @@ -253,8 +261,7 @@ private async Task InjectNewHttp3ConnectionAsync(RequestQueue. HttpAuthority? authority = null; HttpConnectionWaiter waiter = queueItem.Waiter; - CancellationTokenSource cts = GetConnectTimeoutCancellationTokenSource(); - waiter.ConnectionCancellationTokenSource = cts; + CancellationTokenSource cts = GetConnectTimeoutCancellationTokenSource(waiter); Activity? connectionSetupActivity = null; try { diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionPool/HttpConnectionPool.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionPool/HttpConnectionPool.cs index 3c91fc4ccde43..fee5a120f8e2e 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionPool/HttpConnectionPool.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionPool/HttpConnectionPool.cs @@ -559,8 +559,8 @@ public async ValueTask SendWithVersionDetectionAndRetryAsyn // We never cancel both attempts at the same time. When downgrade happens, it's possible that both waiters are non-null, // but in that case http2ConnectionWaiter.ConnectionCancellationTokenSource shall be null. Debug.Assert(http11ConnectionWaiter is null || http2ConnectionWaiter?.ConnectionCancellationTokenSource is null); - http11ConnectionWaiter?.CancelIfNecessary(this, cancellationToken.IsCancellationRequested); - http2ConnectionWaiter?.CancelIfNecessary(this, cancellationToken.IsCancellationRequested); + http11ConnectionWaiter?.SetTimeoutToPendingConnectionAttempt(this, cancellationToken.IsCancellationRequested); + http2ConnectionWaiter?.SetTimeoutToPendingConnectionAttempt(this, cancellationToken.IsCancellationRequested); } } } @@ -827,7 +827,31 @@ private async ValueTask EstablishSocksTunnel(HttpRequestMessage request, return stream; } - private CancellationTokenSource GetConnectTimeoutCancellationTokenSource() => new CancellationTokenSource(Settings._connectTimeout); + private CancellationTokenSource GetConnectTimeoutCancellationTokenSource(HttpConnectionWaiter waiter) + where T : HttpConnectionBase? + { + var cts = new CancellationTokenSource(Settings._connectTimeout); + + lock (waiter) + { + // After a request completes (or is canceled), it will call into SetTimeoutToPendingConnectionAttempt, + // which will no-op if ConnectionCancellationTokenSource is not set, assuming that the connection attempt is done. + // As the initiating request for this connection attempt may complete concurrently at any time, + // there is a race condition where the first call to SetTimeoutToPendingConnectionAttempt may happen + // before we were able to set the CTS, so no timeout will be applied even though the request is already done. + waiter.ConnectionCancellationTokenSource = cts; + + // To fix that, we check whether the waiter already completed now that we're holding a lock. + // If it had, call SetTimeoutToPendingConnectionAttempt again now that the CTS is set. + if (waiter.Task.IsCompleted) + { + waiter.SetTimeoutToPendingConnectionAttempt(this, requestCancelled: waiter.Task.IsCanceled); + waiter.ConnectionCancellationTokenSource = null; + } + } + + return cts; + } private static Exception CreateConnectTimeoutException(OperationCanceledException oce) { diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionPool/HttpConnectionWaiter.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionPool/HttpConnectionWaiter.cs index 9e1e0f7127d68..48c8d6a29a550 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionPool/HttpConnectionWaiter.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectionPool/HttpConnectionWaiter.cs @@ -75,7 +75,7 @@ public bool TrySignal(T connection) } } - public void CancelIfNecessary(HttpConnectionPool pool, bool requestCancelled) + public void SetTimeoutToPendingConnectionAttempt(HttpConnectionPool pool, bool requestCancelled) { int timeout = GlobalHttpSettings.SocketsHttpHandler.PendingConnectionTimeoutOnRequestCompletion; if (ConnectionCancellationTokenSource is null || diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs index ae1669c18e68b..9ae4d15f64cd7 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Cancellation.cs @@ -393,6 +393,68 @@ await RemoteExecutor.Invoke(static async (versionString, timoutStr) => }, UseVersion.ToString(), timeout.ToString()).DisposeAsync(); } + [OuterLoop("We wait for PendingConnectionTimeout which defaults to 5 seconds.")] + [Fact] + public async Task PendingConnectionTimeout_SignalsAllConnectionAttempts() + { + if (UseVersion == HttpVersion.Version30) + { + // HTTP3 does not support ConnectCallback + return; + } + + int pendingConnectionAttempts = 0; + bool connectionAttemptTimedOut = false; + + using var handler = new SocketsHttpHandler + { + ConnectCallback = async (context, cancellation) => + { + Interlocked.Increment(ref pendingConnectionAttempts); + try + { + await Assert.ThrowsAsync(() => Task.Delay(-1, cancellation)).WaitAsync(TestHelper.PassingTestTimeout); + cancellation.ThrowIfCancellationRequested(); + throw new UnreachableException(); + } + catch (TimeoutException) + { + connectionAttemptTimedOut = true; + throw; + } + finally + { + Interlocked.Decrement(ref pendingConnectionAttempts); + } + } + }; + + using HttpClient client = CreateHttpClient(handler); + client.Timeout = TimeSpan.FromSeconds(2); + + // Many of these requests should trigger new connection attempts, and all of those should eventually be cleaned up. + await Parallel.ForAsync(0, 100, async (_, _) => + { + await Assert.ThrowsAnyAsync(() => client.GetAsync("https://dummy")); + }); + + Stopwatch stopwatch = Stopwatch.StartNew(); + + while (Volatile.Read(ref pendingConnectionAttempts) > 0) + { + Assert.False(connectionAttemptTimedOut); + + if (stopwatch.Elapsed > 2 * TestHelper.PassingTestTimeout) + { + Assert.Fail("Connection attempts took too long to get cleaned up"); + } + + await Task.Delay(100); + } + + Assert.False(connectionAttemptTimedOut); + } + private sealed class SetTcsContent : StreamContent { private readonly TaskCompletionSource _tcs; From ddc786706341544a2e0fb209862b31b4d1d34b6f Mon Sep 17 00:00:00 2001 From: Vladimir Sadov Date: Wed, 18 Dec 2024 22:37:04 -0800 Subject: [PATCH 17/25] Remove uses of DECODE_RETURN_KIND part of GCInfo (#110799) * make hijack frames leaf frames * not using DECODE_RETURN_KIND in NativeAOT * delete DECODE_RETURN_KIND * Fix for unix (x64) * fixes for AOT * arm64 fixes * fix for RISC platforms * fix arm32 build * remove unnecessary now PTFF_RAX_IS_GCREF and similar. * fix fir arm32 * revert to the former value of DECODE_HAS_TAILCALLS - in case there are dependencies in diagnostics. --- src/coreclr/gcdump/gcdumpnonx86.cpp | 4 - src/coreclr/inc/eetwain.h | 4 +- src/coreclr/inc/gcinfodecoder.h | 3 - src/coreclr/nativeaot/Runtime/AsmOffsets.h | 8 +- src/coreclr/nativeaot/Runtime/ICodeManager.h | 125 ++---------------- .../nativeaot/Runtime/StackFrameIterator.cpp | 43 +++--- .../nativeaot/Runtime/StackFrameIterator.h | 8 +- .../nativeaot/Runtime/amd64/AsmMacros.inc | 3 +- .../nativeaot/Runtime/amd64/AsmOffsetsCpu.h | 20 +-- src/coreclr/nativeaot/Runtime/amd64/GcProbe.S | 13 +- .../nativeaot/Runtime/amd64/GcProbe.asm | 13 +- .../nativeaot/Runtime/arm/AsmOffsetsCpu.h | 10 +- src/coreclr/nativeaot/Runtime/arm/GcProbe.S | 6 +- .../nativeaot/Runtime/arm64/AsmMacros.h | 11 +- .../nativeaot/Runtime/arm64/AsmOffsetsCpu.h | 10 +- src/coreclr/nativeaot/Runtime/arm64/GcProbe.S | 10 +- .../nativeaot/Runtime/arm64/GcProbe.asm | 11 +- .../nativeaot/Runtime/i386/AsmMacros.inc | 2 - src/coreclr/nativeaot/Runtime/inc/rhbinder.h | 29 ++-- .../Runtime/loongarch64/AsmOffsetsCpu.h | 10 +- .../nativeaot/Runtime/loongarch64/GcProbe.S | 12 +- src/coreclr/nativeaot/Runtime/thread.cpp | 33 +++-- src/coreclr/nativeaot/Runtime/thread.h | 2 +- .../Runtime/unix/UnixNativeCodeManager.cpp | 51 +++---- .../Runtime/unix/UnixNativeCodeManager.h | 3 +- .../Runtime/unix/unixasmmacrosamd64.inc | 1 + .../Runtime/unix/unixasmmacrosarm.inc | 3 +- .../Runtime/unix/unixasmmacrosarm64.inc | 3 +- .../Runtime/unix/unixasmmacrosloongarch64.inc | 2 +- .../Runtime/windows/CoffNativeCodeManager.cpp | 41 +++--- .../Runtime/windows/CoffNativeCodeManager.h | 10 +- src/coreclr/vm/arm/stubs.cpp | 1 + src/coreclr/vm/arm64/stubs.cpp | 4 + src/coreclr/vm/eetwain.cpp | 5 +- src/coreclr/vm/frames.cpp | 6 +- src/coreclr/vm/frames.h | 15 +++ src/coreclr/vm/gcinfodecoder.cpp | 18 +-- src/coreclr/vm/loongarch64/stubs.cpp | 4 + src/coreclr/vm/riscv64/stubs.cpp | 5 + src/coreclr/vm/threads.cpp | 3 + src/coreclr/vm/threads.h | 8 +- src/coreclr/vm/threadsuspend.cpp | 27 ++-- 42 files changed, 224 insertions(+), 376 deletions(-) diff --git a/src/coreclr/gcdump/gcdumpnonx86.cpp b/src/coreclr/gcdump/gcdumpnonx86.cpp index 919bf60d88670..d4366069f14a7 100644 --- a/src/coreclr/gcdump/gcdumpnonx86.cpp +++ b/src/coreclr/gcdump/gcdumpnonx86.cpp @@ -361,7 +361,6 @@ size_t GCDump::DumpGCTable(PTR_CBYTE gcInfoBlock, | DECODE_GENERICS_INST_CONTEXT | DECODE_GC_LIFETIMES | DECODE_PROLOG_LENGTH - | DECODE_RETURN_KIND #if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) | DECODE_HAS_TAILCALLS #endif @@ -502,9 +501,6 @@ size_t GCDump::DumpGCTable(PTR_CBYTE gcInfoBlock, gcPrintf("Size of parameter area: %x\n", hdrdecoder.GetSizeOfStackParameterArea()); #endif - ReturnKind returnKind = hdrdecoder.GetReturnKind(); - gcPrintf("Return Kind: %s\n", ReturnKindToString(returnKind)); - UINT32 cbEncodedMethodSize = hdrdecoder.GetCodeLength(); gcPrintf("Code size: %x\n", cbEncodedMethodSize); diff --git a/src/coreclr/inc/eetwain.h b/src/coreclr/inc/eetwain.h index 271e0de8ecb7f..32801bd23c540 100644 --- a/src/coreclr/inc/eetwain.h +++ b/src/coreclr/inc/eetwain.h @@ -306,7 +306,7 @@ virtual size_t GetFunctionSize(GCInfoToken gcInfoToken) = 0; * returns true. * If hijacking is not possible for some reason, it return false. */ -virtual bool GetReturnAddressHijackInfo(GCInfoToken gcInfoToken, ReturnKind * returnKind) = 0; +virtual bool GetReturnAddressHijackInfo(GCInfoToken gcInfoToken X86_ARG(ReturnKind * returnKind)) = 0; #ifndef USE_GC_INFO_DECODER /* @@ -575,7 +575,7 @@ size_t GetFunctionSize(GCInfoToken gcInfoToken); * returns true. * If hijacking is not possible for some reason, it return false. */ -virtual bool GetReturnAddressHijackInfo(GCInfoToken gcInfoToken, ReturnKind * returnKind); +virtual bool GetReturnAddressHijackInfo(GCInfoToken gcInfoToken X86_ARG(ReturnKind * returnKind)); #ifndef USE_GC_INFO_DECODER /* diff --git a/src/coreclr/inc/gcinfodecoder.h b/src/coreclr/inc/gcinfodecoder.h index d91e10bc081d2..0b51833ee19d5 100644 --- a/src/coreclr/inc/gcinfodecoder.h +++ b/src/coreclr/inc/gcinfodecoder.h @@ -222,7 +222,6 @@ enum GcInfoDecoderFlags DECODE_PROLOG_LENGTH = 0x400, // length of the prolog (used to avoid reporting generics context) DECODE_EDIT_AND_CONTINUE = 0x800, DECODE_REVERSE_PINVOKE_VAR = 0x1000, - DECODE_RETURN_KIND = 0x2000, #if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) DECODE_HAS_TAILCALLS = 0x4000, #endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 @@ -582,7 +581,6 @@ class GcInfoDecoder #if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) bool HasTailCalls(); #endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 || defined(TARGET_RISCV64) - ReturnKind GetReturnKind(); UINT32 GetCodeLength(); UINT32 GetStackBaseRegister(); UINT32 GetSizeOfEditAndContinuePreservedArea(); @@ -615,7 +613,6 @@ class GcInfoDecoder #ifdef TARGET_ARM64 UINT32 m_SizeOfEditAndContinueFixedStackFrame; #endif - ReturnKind m_ReturnKind; #ifdef PARTIALLY_INTERRUPTIBLE_GC_SUPPORTED UINT32 m_NumSafePoints; UINT32 m_SafePointIndex; diff --git a/src/coreclr/nativeaot/Runtime/AsmOffsets.h b/src/coreclr/nativeaot/Runtime/AsmOffsets.h index cb6bf8842e04b..ef5031560bdc4 100644 --- a/src/coreclr/nativeaot/Runtime/AsmOffsets.h +++ b/src/coreclr/nativeaot/Runtime/AsmOffsets.h @@ -52,9 +52,11 @@ ASM_OFFSET( 30, 48, Thread, m_pTransitionFrame) ASM_OFFSET( 34, 50, Thread, m_pDeferredTransitionFrame) ASM_OFFSET( 44, 70, Thread, m_ppvHijackedReturnAddressLocation) ASM_OFFSET( 48, 78, Thread, m_pvHijackedReturnAddress) -ASM_OFFSET( 4c, 80, Thread, m_uHijackedReturnValueFlags) -ASM_OFFSET( 50, 88, Thread, m_pExInfoStackHead) -ASM_OFFSET( 54, 90, Thread, m_threadAbortException) +ASM_OFFSET( 4c, 80, Thread, m_pExInfoStackHead) +ASM_OFFSET( 50, 88, Thread, m_threadAbortException) +#ifdef TARGET_X86 +ASM_OFFSET( 54, FF, Thread, m_uHijackedReturnValueFlags) +#endif ASM_SIZEOF( 14, 20, EHEnum) diff --git a/src/coreclr/nativeaot/Runtime/ICodeManager.h b/src/coreclr/nativeaot/Runtime/ICodeManager.h index f88304a40e868..0fd8f60a5458f 100644 --- a/src/coreclr/nativeaot/Runtime/ICodeManager.h +++ b/src/coreclr/nativeaot/Runtime/ICodeManager.h @@ -28,92 +28,11 @@ enum GCRefKind : unsigned char GCRK_Scalar = 0x00, GCRK_Object = 0x01, GCRK_Byref = 0x02, -#ifdef TARGET_64BIT - // Composite return kinds for value types returned in two registers (encoded with two bits per register) - GCRK_Scalar_Obj = (GCRK_Object << 2) | GCRK_Scalar, - GCRK_Obj_Obj = (GCRK_Object << 2) | GCRK_Object, - GCRK_Byref_Obj = (GCRK_Object << 2) | GCRK_Byref, - GCRK_Scalar_Byref = (GCRK_Byref << 2) | GCRK_Scalar, - GCRK_Obj_Byref = (GCRK_Byref << 2) | GCRK_Object, - GCRK_Byref_Byref = (GCRK_Byref << 2) | GCRK_Byref, - - GCRK_LastValid = GCRK_Byref_Byref, -#else // TARGET_ARM64 GCRK_LastValid = GCRK_Byref, -#endif // TARGET_ARM64 GCRK_Unknown = 0xFF, }; -#ifdef TARGET_ARM64 -// Verify that we can use bitwise shifts to convert from GCRefKind to PInvokeTransitionFrameFlags and back -C_ASSERT(PTFF_X0_IS_GCREF == ((uint64_t)GCRK_Object << 32)); -C_ASSERT(PTFF_X0_IS_BYREF == ((uint64_t)GCRK_Byref << 32)); -C_ASSERT(PTFF_X1_IS_GCREF == ((uint64_t)GCRK_Scalar_Obj << 32)); -C_ASSERT(PTFF_X1_IS_BYREF == ((uint64_t)GCRK_Scalar_Byref << 32)); - -inline uint64_t ReturnKindToTransitionFrameFlags(GCRefKind returnKind) -{ - // just need to report gc ref bits here. - // appropriate PTFF_SAVE_ bits will be added by the frame building routine. - return ((uint64_t)returnKind << 32); -} - -inline GCRefKind TransitionFrameFlagsToReturnKind(uint64_t transFrameFlags) -{ - GCRefKind returnKind = (GCRefKind)((transFrameFlags & (PTFF_X0_IS_GCREF | PTFF_X0_IS_BYREF | PTFF_X1_IS_GCREF | PTFF_X1_IS_BYREF)) >> 32); - ASSERT((returnKind == GCRK_Scalar) || ((transFrameFlags & PTFF_SAVE_X0) && (transFrameFlags & PTFF_SAVE_X1))); - return returnKind; -} - -#elif defined(TARGET_LOONGARCH64) -// Verify that we can use bitwise shifts to convert from GCRefKind to PInvokeTransitionFrameFlags and back -C_ASSERT(PTFF_R4_IS_GCREF == ((uint64_t)GCRK_Object << 31)); -C_ASSERT(PTFF_R4_IS_BYREF == ((uint64_t)GCRK_Byref << 31)); -C_ASSERT(PTFF_R5_IS_GCREF == ((uint64_t)GCRK_Scalar_Obj << 31)); -C_ASSERT(PTFF_R5_IS_BYREF == ((uint64_t)GCRK_Scalar_Byref << 31)); - -inline uint64_t ReturnKindToTransitionFrameFlags(GCRefKind returnKind) -{ - // just need to report gc ref bits here. - // appropriate PTFF_SAVE_ bits will be added by the frame building routine. - return ((uint64_t)returnKind << 31); -} - -inline GCRefKind TransitionFrameFlagsToReturnKind(uint64_t transFrameFlags) -{ - GCRefKind returnKind = (GCRefKind)((transFrameFlags & (PTFF_R4_IS_GCREF | PTFF_R4_IS_BYREF | PTFF_R5_IS_GCREF | PTFF_R5_IS_BYREF)) >> 31); - ASSERT((returnKind == GCRK_Scalar) || ((transFrameFlags & PTFF_SAVE_R4) && (transFrameFlags & PTFF_SAVE_R5))); - return returnKind; -} - -#elif defined(TARGET_AMD64) - -// Verify that we can use bitwise shifts to convert from GCRefKind to PInvokeTransitionFrameFlags and back -C_ASSERT(PTFF_RAX_IS_GCREF == ((uint64_t)GCRK_Object << 16)); -C_ASSERT(PTFF_RAX_IS_BYREF == ((uint64_t)GCRK_Byref << 16)); -C_ASSERT(PTFF_RDX_IS_GCREF == ((uint64_t)GCRK_Scalar_Obj << 16)); -C_ASSERT(PTFF_RDX_IS_BYREF == ((uint64_t)GCRK_Scalar_Byref << 16)); - -inline uint64_t ReturnKindToTransitionFrameFlags(GCRefKind returnKind) -{ - // just need to report gc ref bits here. - // appropriate PTFF_SAVE_ bits will be added by the frame building routine. - return ((uint64_t)returnKind << 16); -} - -inline GCRefKind TransitionFrameFlagsToReturnKind(uint64_t transFrameFlags) -{ - GCRefKind returnKind = (GCRefKind)((transFrameFlags & (PTFF_RAX_IS_GCREF | PTFF_RAX_IS_BYREF | PTFF_RDX_IS_GCREF | PTFF_RDX_IS_BYREF)) >> 16); -#if defined(TARGET_UNIX) - ASSERT((returnKind == GCRK_Scalar) || ((transFrameFlags & PTFF_SAVE_RAX) && (transFrameFlags & PTFF_SAVE_RDX))); -#else - ASSERT((returnKind == GCRK_Scalar) || (transFrameFlags & PTFF_SAVE_RAX)); -#endif - return returnKind; -} - -#elif defined(TARGET_X86) - +#if defined(TARGET_X86) // Verify that we can use bitwise shifts to convert from GCRefKind to PInvokeTransitionFrameFlags and back C_ASSERT(PTFF_RAX_IS_GCREF == ((uint64_t)GCRK_Object << 16)); C_ASSERT(PTFF_RAX_IS_BYREF == ((uint64_t)GCRK_Byref << 16)); @@ -132,40 +51,13 @@ inline GCRefKind TransitionFrameFlagsToReturnKind(uintptr_t transFrameFlags) return returnKind; } -#elif defined(TARGET_ARM) - -// Verify that we can use bitwise shifts to convert from GCRefKind to PInvokeTransitionFrameFlags and back -C_ASSERT(PTFF_R0_IS_GCREF == ((uint64_t)GCRK_Object << 14)); -C_ASSERT(PTFF_R0_IS_BYREF == ((uint64_t)GCRK_Byref << 14)); - -inline uint64_t ReturnKindToTransitionFrameFlags(GCRefKind returnKind) -{ - // just need to report gc ref bits here. - // appropriate PTFF_SAVE_ bits will be added by the frame building routine. - return ((uint64_t)returnKind << 14); -} - -inline GCRefKind TransitionFrameFlagsToReturnKind(uint64_t transFrameFlags) -{ - GCRefKind returnKind = (GCRefKind)((transFrameFlags & (PTFF_R0_IS_GCREF | PTFF_R0_IS_BYREF)) >> 14); - ASSERT((returnKind == GCRK_Scalar) || (transFrameFlags & PTFF_SAVE_R0)); - return returnKind; -} - -#endif - // Extract individual GCRefKind components from a composite return kind -inline GCRefKind ExtractReg0ReturnKind(GCRefKind returnKind) +inline GCRefKind ExtractReturnKind(GCRefKind returnKind) { ASSERT(returnKind <= GCRK_LastValid); return (GCRefKind)(returnKind & (GCRK_Object | GCRK_Byref)); } - -inline GCRefKind ExtractReg1ReturnKind(GCRefKind returnKind) -{ - ASSERT(returnKind <= GCRK_LastValid); - return (GCRefKind)(returnKind >> 2); -} +#endif // // MethodInfo is placeholder type used to allocate space for MethodInfo. Maximum size @@ -272,9 +164,14 @@ class ICodeManager virtual bool IsUnwindable(PTR_VOID pvAddress) PURE_VIRTUAL virtual bool GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, - REGDISPLAY * pRegisterSet, // in - PTR_PTR_VOID * ppvRetAddrLocation, // out - GCRefKind * pRetValueKind) PURE_VIRTUAL // out + REGDISPLAY * pRegisterSet, // in + PTR_PTR_VOID * ppvRetAddrLocation // out + ) PURE_VIRTUAL + +#ifdef TARGET_X86 + virtual GCRefKind GetReturnValueKind(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet) PURE_VIRTUAL +#endif virtual PTR_VOID RemapHardwareFaultToGCSafePoint(MethodInfo * pMethodInfo, PTR_VOID controlPC) PURE_VIRTUAL diff --git a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp index f2f208c89174c..fab1a9b6f6618 100644 --- a/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp @@ -77,10 +77,18 @@ StackFrameIterator::StackFrameIterator(Thread * pThreadToWalk, PInvokeTransition { InternalInit(pThreadToWalk, pThreadToWalk->GetInterruptedContext(), GcStackWalkFlags | ActiveStackFrame); } - else + else if (pInitialTransitionFrame == TOP_OF_STACK_MARKER) { InternalInit(pThreadToWalk, pInitialTransitionFrame, GcStackWalkFlags); } + else + { + uint32_t flags = (pInitialTransitionFrame->m_Flags & PTFF_THREAD_HIJACK) == 0 ? + GcStackWalkFlags : + GcStackWalkFlags | ActiveStackFrame; + + InternalInit(pThreadToWalk, pInitialTransitionFrame, flags); + } PrepareToYieldFrame(); } @@ -110,8 +118,10 @@ void StackFrameIterator::EnterInitialInvalidState(Thread * pThreadToWalk) m_pThread = pThreadToWalk; m_pInstance = GetRuntimeInstance(); m_pCodeManager = NULL; +#ifdef TARGET_X86 m_pHijackedReturnValue = NULL; m_HijackedReturnValueKind = GCRK_Unknown; +#endif m_pConservativeStackRangeLowerBound = NULL; m_pConservativeStackRangeUpperBound = NULL; m_pendingFuncletFramePointer = NULL; @@ -180,17 +190,6 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PInvokeTransitionF if (pFrame->m_Flags & PTFF_SAVE_R3) { m_RegDisplay.pR3 = pPreservedRegsCursor++; } if (pFrame->m_Flags & PTFF_SAVE_LR) { m_RegDisplay.pLR = pPreservedRegsCursor++; } - if (pFrame->m_Flags & PTFF_R0_IS_GCREF) - { - m_pHijackedReturnValue = (PTR_OBJECTREF) m_RegDisplay.pR0; - m_HijackedReturnValueKind = GCRK_Object; - } - if (pFrame->m_Flags & PTFF_R0_IS_BYREF) - { - m_pHijackedReturnValue = (PTR_OBJECTREF) m_RegDisplay.pR0; - m_HijackedReturnValueKind = GCRK_Byref; - } - #elif defined(TARGET_ARM64) m_RegDisplay.pFP = (PTR_uintptr_t)PTR_HOST_MEMBER_TADDR(PInvokeTransitionFrame, pFrame, m_FramePointer); m_RegDisplay.pLR = (PTR_uintptr_t)PTR_HOST_MEMBER_TADDR(PInvokeTransitionFrame, pFrame, m_RIP); @@ -232,13 +231,6 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PInvokeTransitionF if (pFrame->m_Flags & PTFF_SAVE_LR) { m_RegDisplay.pLR = pPreservedRegsCursor++; } - GCRefKind retValueKind = TransitionFrameFlagsToReturnKind(pFrame->m_Flags); - if (retValueKind != GCRK_Scalar) - { - m_pHijackedReturnValue = (PTR_OBJECTREF)m_RegDisplay.pX0; - m_HijackedReturnValueKind = retValueKind; - } - #elif defined(TARGET_LOONGARCH64) m_RegDisplay.pFP = (PTR_uintptr_t)PTR_HOST_MEMBER_TADDR(PInvokeTransitionFrame, pFrame, m_FramePointer); m_RegDisplay.pRA = (PTR_uintptr_t)PTR_HOST_MEMBER_TADDR(PInvokeTransitionFrame, pFrame, m_RIP); @@ -280,13 +272,6 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PInvokeTransitionF if (pFrame->m_Flags & PTFF_SAVE_RA) { m_RegDisplay.pRA = pPreservedRegsCursor++; } - GCRefKind retValueKind = TransitionFrameFlagsToReturnKind(pFrame->m_Flags); - if (retValueKind != GCRK_Scalar) - { - m_pHijackedReturnValue = (PTR_OBJECTREF)m_RegDisplay.pR4; - m_HijackedReturnValueKind = retValueKind; - } - #else // TARGET_ARM if (pFrame->m_Flags & PTFF_SAVE_RBX) { m_RegDisplay.pRbx = pPreservedRegsCursor++; } if (pFrame->m_Flags & PTFF_SAVE_RSI) { m_RegDisplay.pRsi = pPreservedRegsCursor++; } @@ -314,12 +299,14 @@ void StackFrameIterator::InternalInit(Thread * pThreadToWalk, PInvokeTransitionF if (pFrame->m_Flags & PTFF_SAVE_R11) { m_RegDisplay.pR11 = pPreservedRegsCursor++; } #endif // TARGET_AMD64 +#ifdef TARGET_X86 GCRefKind retValueKind = TransitionFrameFlagsToReturnKind(pFrame->m_Flags); if (retValueKind != GCRK_Scalar) { m_pHijackedReturnValue = (PTR_OBJECTREF)m_RegDisplay.pRax; m_HijackedReturnValueKind = retValueKind; } +#endif #endif // TARGET_ARM @@ -1529,8 +1516,10 @@ void StackFrameIterator::NextInternal() m_dwFlags &= ~(ExCollide|MethodStateCalculated|UnwoundReversePInvoke|ActiveStackFrame); ASSERT(IsValid()); +#ifdef TARGET_X86 m_pHijackedReturnValue = NULL; m_HijackedReturnValueKind = GCRK_Unknown; +#endif #ifdef _DEBUG SetControlPC(dac_cast((void*)666)); @@ -1944,6 +1933,7 @@ void StackFrameIterator::CalculateCurrentMethodState() m_dwFlags |= MethodStateCalculated; } +#ifdef TARGET_X86 bool StackFrameIterator::GetHijackedReturnValueLocation(PTR_OBJECTREF * pLocation, GCRefKind * pKind) { if (GCRK_Unknown == m_HijackedReturnValueKind) @@ -1955,6 +1945,7 @@ bool StackFrameIterator::GetHijackedReturnValueLocation(PTR_OBJECTREF * pLocatio *pKind = m_HijackedReturnValueKind; return true; } +#endif void StackFrameIterator::SetControlPC(PTR_VOID controlPC) { diff --git a/src/coreclr/nativeaot/Runtime/StackFrameIterator.h b/src/coreclr/nativeaot/Runtime/StackFrameIterator.h index f174edd4c473b..c3fecb9f84e29 100644 --- a/src/coreclr/nativeaot/Runtime/StackFrameIterator.h +++ b/src/coreclr/nativeaot/Runtime/StackFrameIterator.h @@ -51,7 +51,9 @@ class StackFrameIterator PTR_ICodeManager GetCodeManager(); MethodInfo * GetMethodInfo(); bool IsActiveStackFrame(); +#ifdef TARGET_X86 bool GetHijackedReturnValueLocation(PTR_OBJECTREF * pLocation, GCRefKind * pKind); +#endif void SetControlPC(PTR_VOID controlPC); static bool IsValidReturnAddress(PTR_VOID pvAddress); @@ -221,10 +223,12 @@ class StackFrameIterator PTR_ICodeManager m_pCodeManager; MethodInfo m_methodInfo; PTR_VOID m_effectiveSafePointAddress; +#ifdef TARGET_X86 PTR_OBJECTREF m_pHijackedReturnValue; GCRefKind m_HijackedReturnValueKind; - PTR_uintptr_t m_pConservativeStackRangeLowerBound; - PTR_uintptr_t m_pConservativeStackRangeUpperBound; +#endif + PTR_uintptr_t m_pConservativeStackRangeLowerBound; + PTR_uintptr_t m_pConservativeStackRangeUpperBound; uint32_t m_dwFlags; PTR_ExInfo m_pNextExInfo; PTR_VOID m_pendingFuncletFramePointer; diff --git a/src/coreclr/nativeaot/Runtime/amd64/AsmMacros.inc b/src/coreclr/nativeaot/Runtime/amd64/AsmMacros.inc index 41c43252317d9..4a3437f2f0618 100644 --- a/src/coreclr/nativeaot/Runtime/amd64/AsmMacros.inc +++ b/src/coreclr/nativeaot/Runtime/amd64/AsmMacros.inc @@ -356,9 +356,8 @@ PTFF_SAVE_ALL_PRESERVED equ 000000F7h ;; NOTE: RBP is not included in this set PTFF_SAVE_RSP equ 00008000h PTFF_SAVE_RAX equ 00000100h ;; RAX is saved in hijack handler - in case it contains a GC ref PTFF_SAVE_ALL_SCRATCH equ 00007F00h -PTFF_RAX_IS_GCREF equ 00010000h ;; iff PTFF_SAVE_RAX: set -> eax is Object, clear -> eax is scalar -PTFF_RAX_IS_BYREF equ 00020000h ;; iff PTFF_SAVE_RAX: set -> eax is ByRef, clear -> eax is Object or scalar PTFF_THREAD_ABORT equ 00100000h ;; indicates that ThreadAbortException should be thrown when returning from the transition +PTFF_THREAD_HIJACK equ 00200000h ;; indicates that this is a frame for a hijacked call ;; These must match the TrapThreadsFlags enum TrapThreadsFlags_None equ 0 diff --git a/src/coreclr/nativeaot/Runtime/amd64/AsmOffsetsCpu.h b/src/coreclr/nativeaot/Runtime/amd64/AsmOffsetsCpu.h index 2a2e85e5e72ba..afeb2a408851a 100644 --- a/src/coreclr/nativeaot/Runtime/amd64/AsmOffsetsCpu.h +++ b/src/coreclr/nativeaot/Runtime/amd64/AsmOffsetsCpu.h @@ -8,7 +8,7 @@ // NOTE: the offsets MUST be in hex notation WITHOUT the 0x prefix #ifndef UNIX_AMD64_ABI -PLAT_ASM_SIZEOF(260, ExInfo) +PLAT_ASM_SIZEOF(250, ExInfo) PLAT_ASM_OFFSET(0, ExInfo, m_pPrevExInfo) PLAT_ASM_OFFSET(8, ExInfo, m_pExContext) PLAT_ASM_OFFSET(10, ExInfo, m_exception) @@ -16,7 +16,7 @@ PLAT_ASM_OFFSET(18, ExInfo, m_kind) PLAT_ASM_OFFSET(19, ExInfo, m_passNumber) PLAT_ASM_OFFSET(1c, ExInfo, m_idxCurClause) PLAT_ASM_OFFSET(20, ExInfo, m_frameIter) -PLAT_ASM_OFFSET(250, ExInfo, m_notifyDebuggerSP) +PLAT_ASM_OFFSET(240, ExInfo, m_notifyDebuggerSP) PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_RIP) PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_FramePointer) @@ -24,12 +24,12 @@ PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_pThread) PLAT_ASM_OFFSET(18, PInvokeTransitionFrame, m_Flags) PLAT_ASM_OFFSET(20, PInvokeTransitionFrame, m_PreservedRegs) -PLAT_ASM_SIZEOF(230, StackFrameIterator) +PLAT_ASM_SIZEOF(220, StackFrameIterator) PLAT_ASM_OFFSET(10, StackFrameIterator, m_FramePointer) PLAT_ASM_OFFSET(18, StackFrameIterator, m_ControlPC) PLAT_ASM_OFFSET(20, StackFrameIterator, m_RegDisplay) -PLAT_ASM_OFFSET(220, StackFrameIterator, m_OriginalControlPC) -PLAT_ASM_OFFSET(228, StackFrameIterator, m_pPreviousTransitionFrame) +PLAT_ASM_OFFSET(210, StackFrameIterator, m_OriginalControlPC) +PLAT_ASM_OFFSET(218, StackFrameIterator, m_pPreviousTransitionFrame) PLAT_ASM_SIZEOF(100, PAL_LIMITED_CONTEXT) PLAT_ASM_OFFSET(0, PAL_LIMITED_CONTEXT, IP) @@ -73,7 +73,7 @@ PLAT_ASM_OFFSET(90, REGDISPLAY, Xmm) #else // !UNIX_AMD64_ABI -PLAT_ASM_SIZEOF(1a0, ExInfo) +PLAT_ASM_SIZEOF(190, ExInfo) PLAT_ASM_OFFSET(0, ExInfo, m_pPrevExInfo) PLAT_ASM_OFFSET(8, ExInfo, m_pExContext) PLAT_ASM_OFFSET(10, ExInfo, m_exception) @@ -81,7 +81,7 @@ PLAT_ASM_OFFSET(18, ExInfo, m_kind) PLAT_ASM_OFFSET(19, ExInfo, m_passNumber) PLAT_ASM_OFFSET(1c, ExInfo, m_idxCurClause) PLAT_ASM_OFFSET(20, ExInfo, m_frameIter) -PLAT_ASM_OFFSET(198, ExInfo, m_notifyDebuggerSP) +PLAT_ASM_OFFSET(188, ExInfo, m_notifyDebuggerSP) PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_RIP) PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_FramePointer) @@ -89,12 +89,12 @@ PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_pThread) PLAT_ASM_OFFSET(18, PInvokeTransitionFrame, m_Flags) PLAT_ASM_OFFSET(20, PInvokeTransitionFrame, m_PreservedRegs) -PLAT_ASM_SIZEOF(178, StackFrameIterator) +PLAT_ASM_SIZEOF(168, StackFrameIterator) PLAT_ASM_OFFSET(10, StackFrameIterator, m_FramePointer) PLAT_ASM_OFFSET(18, StackFrameIterator, m_ControlPC) PLAT_ASM_OFFSET(20, StackFrameIterator, m_RegDisplay) -PLAT_ASM_OFFSET(168, StackFrameIterator, m_OriginalControlPC) -PLAT_ASM_OFFSET(170, StackFrameIterator, m_pPreviousTransitionFrame) +PLAT_ASM_OFFSET(158, StackFrameIterator, m_OriginalControlPC) +PLAT_ASM_OFFSET(160, StackFrameIterator, m_pPreviousTransitionFrame) PLAT_ASM_SIZEOF(50, PAL_LIMITED_CONTEXT) PLAT_ASM_OFFSET(0, PAL_LIMITED_CONTEXT, IP) diff --git a/src/coreclr/nativeaot/Runtime/amd64/GcProbe.S b/src/coreclr/nativeaot/Runtime/amd64/GcProbe.S index d8a08e76f4588..ef4709055f010 100644 --- a/src/coreclr/nativeaot/Runtime/amd64/GcProbe.S +++ b/src/coreclr/nativeaot/Runtime/amd64/GcProbe.S @@ -76,7 +76,6 @@ // // Register state on exit: // R11: thread pointer -// RCX: return value flags // RAX, RDX preserved, other volatile regs trashed // .macro FixupHijackedCallstack @@ -95,14 +94,10 @@ mov rcx, [r11 + OFFSETOF__Thread__m_pvHijackedReturnAddress] push rcx - // Fetch the return address flags - mov rcx, [r11 + OFFSETOF__Thread__m_uHijackedReturnValueFlags] - // Clear hijack state - xor r9, r9 - mov [r11 + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], r9 - mov [r11 + OFFSETOF__Thread__m_pvHijackedReturnAddress], r9 - mov [r11 + OFFSETOF__Thread__m_uHijackedReturnValueFlags], r9 + xor ecx, ecx + mov [r11 + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], rcx + mov [r11 + OFFSETOF__Thread__m_pvHijackedReturnAddress], rcx .endm // @@ -117,7 +112,7 @@ NESTED_ENTRY RhpGcProbeHijack, _TEXT, NoHandler ret LOCAL_LABEL(WaitForGC): - or ecx, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_SAVE_RDX + mov ecx, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_SAVE_RDX + PTFF_THREAD_HIJACK jmp C_FUNC(RhpWaitForGC) NESTED_END RhpGcProbeHijack, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/amd64/GcProbe.asm b/src/coreclr/nativeaot/Runtime/amd64/GcProbe.asm index 531762eedd8da..7410d851d5a1c 100644 --- a/src/coreclr/nativeaot/Runtime/amd64/GcProbe.asm +++ b/src/coreclr/nativeaot/Runtime/amd64/GcProbe.asm @@ -76,7 +76,6 @@ endm ;; ;; Register state on exit: ;; RDX: thread pointer -;; RCX: return value flags ;; RAX: preserved, other volatile regs trashed ;; FixupHijackedCallstack macro @@ -87,14 +86,10 @@ FixupHijackedCallstack macro mov rcx, [rdx + OFFSETOF__Thread__m_pvHijackedReturnAddress] push rcx - ;; Fetch the return address flags - mov rcx, [rdx + OFFSETOF__Thread__m_uHijackedReturnValueFlags] - ;; Clear hijack state - xor r9, r9 - mov [rdx + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], r9 - mov [rdx + OFFSETOF__Thread__m_pvHijackedReturnAddress], r9 - mov [rdx + OFFSETOF__Thread__m_uHijackedReturnValueFlags], r9 + xor ecx, ecx + mov [rdx + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], rcx + mov [rdx + OFFSETOF__Thread__m_pvHijackedReturnAddress], rcx endm EXTERN RhpPInvokeExceptionGuard : PROC @@ -110,7 +105,7 @@ NESTED_ENTRY RhpGcProbeHijack, _TEXT, RhpPInvokeExceptionGuard jnz @f ret @@: - or ecx, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + mov ecx, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_RAX + PTFF_THREAD_HIJACK jmp RhpWaitForGC NESTED_END RhpGcProbeHijack, _TEXT diff --git a/src/coreclr/nativeaot/Runtime/arm/AsmOffsetsCpu.h b/src/coreclr/nativeaot/Runtime/arm/AsmOffsetsCpu.h index ab5b991274b42..5e7a0203df889 100644 --- a/src/coreclr/nativeaot/Runtime/arm/AsmOffsetsCpu.h +++ b/src/coreclr/nativeaot/Runtime/arm/AsmOffsetsCpu.h @@ -7,7 +7,7 @@ // // NOTE: the offsets MUST be in hex notation WITHOUT the 0x prefix -PLAT_ASM_SIZEOF(130, ExInfo) +PLAT_ASM_SIZEOF(128, ExInfo) PLAT_ASM_OFFSET(0, ExInfo, m_pPrevExInfo) PLAT_ASM_OFFSET(4, ExInfo, m_pExContext) PLAT_ASM_OFFSET(8, ExInfo, m_exception) @@ -15,7 +15,7 @@ PLAT_ASM_OFFSET(0c, ExInfo, m_kind) PLAT_ASM_OFFSET(0d, ExInfo, m_passNumber) PLAT_ASM_OFFSET(10, ExInfo, m_idxCurClause) PLAT_ASM_OFFSET(18, ExInfo, m_frameIter) -PLAT_ASM_OFFSET(128, ExInfo, m_notifyDebuggerSP) +PLAT_ASM_OFFSET(120, ExInfo, m_notifyDebuggerSP) PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_FramePointer) PLAT_ASM_OFFSET(4, PInvokeTransitionFrame, m_RIP) @@ -23,12 +23,12 @@ PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_pThread) PLAT_ASM_OFFSET(c, PInvokeTransitionFrame, m_Flags) PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_PreservedRegs) -PLAT_ASM_SIZEOF(110, StackFrameIterator) +PLAT_ASM_SIZEOF(108, StackFrameIterator) PLAT_ASM_OFFSET(08, StackFrameIterator, m_FramePointer) PLAT_ASM_OFFSET(0c, StackFrameIterator, m_ControlPC) PLAT_ASM_OFFSET(10, StackFrameIterator, m_RegDisplay) -PLAT_ASM_OFFSET(108, StackFrameIterator, m_OriginalControlPC) -PLAT_ASM_OFFSET(10c, StackFrameIterator, m_pPreviousTransitionFrame) +PLAT_ASM_OFFSET(100, StackFrameIterator, m_OriginalControlPC) +PLAT_ASM_OFFSET(104, StackFrameIterator, m_pPreviousTransitionFrame) PLAT_ASM_SIZEOF(70, PAL_LIMITED_CONTEXT) PLAT_ASM_OFFSET(24, PAL_LIMITED_CONTEXT, IP) diff --git a/src/coreclr/nativeaot/Runtime/arm/GcProbe.S b/src/coreclr/nativeaot/Runtime/arm/GcProbe.S index 8277d9035b0d0..0092be687bcec 100644 --- a/src/coreclr/nativeaot/Runtime/arm/GcProbe.S +++ b/src/coreclr/nativeaot/Runtime/arm/GcProbe.S @@ -75,13 +75,11 @@ // Fix the stack by restoring the original return address ldr lr, [r2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] - ldr r12, [r2, #OFFSETOF__Thread__m_uHijackedReturnValueFlags] // Clear hijack state mov r3, #0 str r3, [r2, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] str r3, [r2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] - str r3, [r2, #OFFSETOF__Thread__m_uHijackedReturnValueFlags] .endm NESTED_ENTRY RhpWaitForGC, _TEXT, NoHandler @@ -126,8 +124,8 @@ NESTED_ENTRY RhpGcProbeHijack, _TEXT, NoHandler bne LOCAL_LABEL(WaitForGC) bx lr LOCAL_LABEL(WaitForGC): - mov r3, #(DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_R0) - orr r12, r3 + mov r12, #(DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_R0) + orr r12, r12, #PTFF_THREAD_HIJACK b RhpWaitForGC NESTED_END RhpGcProbeHijack diff --git a/src/coreclr/nativeaot/Runtime/arm64/AsmMacros.h b/src/coreclr/nativeaot/Runtime/arm64/AsmMacros.h index 2f6e83e2cf9b6..90e1b5d777994 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/AsmMacros.h +++ b/src/coreclr/nativeaot/Runtime/arm64/AsmMacros.h @@ -60,17 +60,10 @@ PTFF_SAVE_ALL_SCRATCH equ 0x3FFFF800 ;; NOTE: X0-X18 PTFF_SAVE_FP equ 0x40000000 PTFF_SAVE_LR equ 0x80000000 -;; NOTE: The following flags represent the upper 32 bits of the PInvokeTransitionFrameFlags. -;; Since the assembler doesn't support 64 bit constants in any way, we need to define just -;; the upper bits here -PTFF_X0_IS_GCREF_HI equ 0x00000001 ;; iff PTFF_SAVE_X0 : set->x0 is Object, clear->x0 is scalar -PTFF_X0_IS_BYREF_HI equ 0x00000002 ;; iff PTFF_SAVE_X0 : set->x0 is ByRef, clear->x0 is Object or scalar -PTFF_X1_IS_GCREF_HI equ 0x00000004 ;; iff PTFF_SAVE_X1 : set->x1 is Object, clear->x1 is scalar -PTFF_X1_IS_BYREF_HI equ 0x00000008 ;; iff PTFF_SAVE_X1 : set->x1 is ByRef, clear->x1 is Object or scalar -PTFF_THREAD_ABORT_HI equ 0x00000010 ;; indicates that ThreadAbortException should be thrown when returning from the transition +PTFF_THREAD_HIJACK_HI equ 0x00000002 // upper 32 bits of the PTFF_THREAD_HIJACK ;; Bit position for the flags above, to be used with tbz / tbnz instructions -PTFF_THREAD_ABORT_BIT equ 36 +PTFF_THREAD_ABORT_BIT equ 32 ;; These must match the TrapThreadsFlags enum TrapThreadsFlags_None equ 0 diff --git a/src/coreclr/nativeaot/Runtime/arm64/AsmOffsetsCpu.h b/src/coreclr/nativeaot/Runtime/arm64/AsmOffsetsCpu.h index 193692feae810..62c34004d7613 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/AsmOffsetsCpu.h +++ b/src/coreclr/nativeaot/Runtime/arm64/AsmOffsetsCpu.h @@ -7,7 +7,7 @@ // // NOTE: the offsets MUST be in hex notation WITHOUT the 0x prefix -PLAT_ASM_SIZEOF(288, ExInfo) +PLAT_ASM_SIZEOF(278, ExInfo) PLAT_ASM_OFFSET(0, ExInfo, m_pPrevExInfo) PLAT_ASM_OFFSET(8, ExInfo, m_pExContext) PLAT_ASM_OFFSET(10, ExInfo, m_exception) @@ -15,7 +15,7 @@ PLAT_ASM_OFFSET(18, ExInfo, m_kind) PLAT_ASM_OFFSET(19, ExInfo, m_passNumber) PLAT_ASM_OFFSET(1c, ExInfo, m_idxCurClause) PLAT_ASM_OFFSET(20, ExInfo, m_frameIter) -PLAT_ASM_OFFSET(280, ExInfo, m_notifyDebuggerSP) +PLAT_ASM_OFFSET(270, ExInfo, m_notifyDebuggerSP) PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_FramePointer) PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_RIP) @@ -23,12 +23,12 @@ PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_pThread) PLAT_ASM_OFFSET(18, PInvokeTransitionFrame, m_Flags) PLAT_ASM_OFFSET(20, PInvokeTransitionFrame, m_PreservedRegs) -PLAT_ASM_SIZEOF(260, StackFrameIterator) +PLAT_ASM_SIZEOF(250, StackFrameIterator) PLAT_ASM_OFFSET(10, StackFrameIterator, m_FramePointer) PLAT_ASM_OFFSET(18, StackFrameIterator, m_ControlPC) PLAT_ASM_OFFSET(20, StackFrameIterator, m_RegDisplay) -PLAT_ASM_OFFSET(250, StackFrameIterator, m_OriginalControlPC) -PLAT_ASM_OFFSET(258, StackFrameIterator, m_pPreviousTransitionFrame) +PLAT_ASM_OFFSET(240, StackFrameIterator, m_OriginalControlPC) +PLAT_ASM_OFFSET(248, StackFrameIterator, m_pPreviousTransitionFrame) PLAT_ASM_SIZEOF(C0, PAL_LIMITED_CONTEXT) diff --git a/src/coreclr/nativeaot/Runtime/arm64/GcProbe.S b/src/coreclr/nativeaot/Runtime/arm64/GcProbe.S index 0a1b71eeae738..b8ae78630a6ea 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/GcProbe.S +++ b/src/coreclr/nativeaot/Runtime/arm64/GcProbe.S @@ -93,7 +93,6 @@ // // Register state on exit: // x2: thread pointer -// x12: transition frame flags for the return registers x0 and x1 // .macro FixupHijackedCallstack @@ -107,17 +106,13 @@ // // Fix the stack by restoring the original return address // - // Load m_pvHijackedReturnAddress and m_uHijackedReturnValueFlags - ldp lr, x12, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + ldr lr, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] // // Clear hijack state // // Clear m_ppvHijackedReturnAddressLocation and m_pvHijackedReturnAddress stp xzr, xzr, [x2, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] - // Clear m_uHijackedReturnValueFlags - str xzr, [x2, #OFFSETOF__Thread__m_uHijackedReturnValueFlags] - .endm // @@ -131,7 +126,8 @@ NESTED_ENTRY RhpGcProbeHijack, _TEXT, NoHandler ret LOCAL_LABEL(WaitForGC): - orr x12, x12, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_X0 + PTFF_SAVE_X1 + mov x12, DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_X0 + PTFF_SAVE_X1 + movk x12, PTFF_THREAD_HIJACK_HI, lsl #32 b C_FUNC(RhpWaitForGC) NESTED_END RhpGcProbeHijack diff --git a/src/coreclr/nativeaot/Runtime/arm64/GcProbe.asm b/src/coreclr/nativeaot/Runtime/arm64/GcProbe.asm index 12de9d9fe9a1d..b9048873ea6de 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/GcProbe.asm +++ b/src/coreclr/nativeaot/Runtime/arm64/GcProbe.asm @@ -105,7 +105,6 @@ PROBE_FRAME_SIZE field 0 ;; Register state on exit: ;; x2: thread pointer ;; x3: trashed -;; x12: transition frame flags for the return registers x0 and x1 ;; MACRO FixupHijackedCallstack @@ -116,9 +115,7 @@ PROBE_FRAME_SIZE field 0 ;; ;; Fix the stack by restoring the original return address ;; - ASSERT OFFSETOF__Thread__m_uHijackedReturnValueFlags == (OFFSETOF__Thread__m_pvHijackedReturnAddress + 8) - ;; Load m_pvHijackedReturnAddress and m_uHijackedReturnValueFlags - ldp lr, x12, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] + ldr lr, [x2, #OFFSETOF__Thread__m_pvHijackedReturnAddress] ;; ;; Clear hijack state @@ -126,9 +123,6 @@ PROBE_FRAME_SIZE field 0 ASSERT OFFSETOF__Thread__m_pvHijackedReturnAddress == (OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation + 8) ;; Clear m_ppvHijackedReturnAddressLocation and m_pvHijackedReturnAddress stp xzr, xzr, [x2, #OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation] - ;; Clear m_uHijackedReturnValueFlags - str xzr, [x2, #OFFSETOF__Thread__m_uHijackedReturnValueFlags] - MEND MACRO @@ -161,7 +155,8 @@ PROBE_FRAME_SIZE field 0 ret WaitForGC - orr x12, x12, #(DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_X0 + PTFF_SAVE_X1) + mov x12, #(DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_X0 + PTFF_SAVE_X1) + movk x12, #PTFF_THREAD_HIJACK_HI, lsl #32 b RhpWaitForGC NESTED_END RhpGcProbeHijackWrapper diff --git a/src/coreclr/nativeaot/Runtime/i386/AsmMacros.inc b/src/coreclr/nativeaot/Runtime/i386/AsmMacros.inc index 9541f73940215..8ee2e79f44fde 100644 --- a/src/coreclr/nativeaot/Runtime/i386/AsmMacros.inc +++ b/src/coreclr/nativeaot/Runtime/i386/AsmMacros.inc @@ -125,8 +125,6 @@ PTFF_SAVE_ALL_PRESERVED equ 00000007h ;; NOTE: RBP is not included in this set PTFF_SAVE_RSP equ 00008000h PTFF_SAVE_RAX equ 00000100h ;; RAX is saved if it contains a GC ref and we're in hijack handler PTFF_SAVE_ALL_SCRATCH equ 00000700h -PTFF_RAX_IS_GCREF equ 00010000h ;; iff PTFF_SAVE_RAX: set -> eax is Object, clear -> eax is scalar -PTFF_RAX_IS_BYREF equ 00020000h ;; iff PTFF_SAVE_RAX: set -> eax is ByRef, clear -> eax is Object or scalar PTFF_THREAD_ABORT equ 00100000h ;; indicates that ThreadAbortException should be thrown when returning from the transition ;; These must match the TrapThreadsFlags enum diff --git a/src/coreclr/nativeaot/Runtime/inc/rhbinder.h b/src/coreclr/nativeaot/Runtime/inc/rhbinder.h index 21a15a01c905e..84f10d2257ea5 100644 --- a/src/coreclr/nativeaot/Runtime/inc/rhbinder.h +++ b/src/coreclr/nativeaot/Runtime/inc/rhbinder.h @@ -271,10 +271,9 @@ enum PInvokeTransitionFrameFlags // a return address pointing into the hijacked method and that method's // lr register, which may hold a gc pointer - PTFF_R0_IS_GCREF = 0x00004000, // used by hijack handler to report return value of hijacked method - PTFF_R0_IS_BYREF = 0x00008000, // used by hijack handler to report return value of hijacked method + PTFF_THREAD_ABORT = 0x00004000, // indicates that ThreadAbortException should be thrown when returning from the transition - PTFF_THREAD_ABORT = 0x00010000, // indicates that ThreadAbortException should be thrown when returning from the transition + PTFF_THREAD_HIJACK = 0x00008000, // indicates that this is a frame for a hijacked call }; #elif defined(TARGET_ARM64) enum PInvokeTransitionFrameFlags : uint64_t @@ -329,13 +328,9 @@ enum PInvokeTransitionFrameFlags : uint64_t // a return address pointing into the hijacked method and that method's // lr register, which may hold a gc pointer - // used by hijack handler to report return value of hijacked method - PTFF_X0_IS_GCREF = 0x0000000100000000, - PTFF_X0_IS_BYREF = 0x0000000200000000, - PTFF_X1_IS_GCREF = 0x0000000400000000, - PTFF_X1_IS_BYREF = 0x0000000800000000, + PTFF_THREAD_ABORT = 0x0000000100000000, // indicates that ThreadAbortException should be thrown when returning from the transition - PTFF_THREAD_ABORT = 0x0000001000000000, // indicates that ThreadAbortException should be thrown when returning from the transition + PTFF_THREAD_HIJACK = 0x0000000200000000, // indicates that this is a frame for a hijacked call }; #elif defined(TARGET_LOONGARCH64) @@ -390,13 +385,9 @@ enum PInvokeTransitionFrameFlags : uint64_t // a return address pointing into the hijacked method and that method's // ra register, which may hold a gc pointer - // used by hijack handler to report return value of hijacked method - PTFF_R4_IS_GCREF = 0x0000000080000000, - PTFF_R4_IS_BYREF = 0x0000000100000000, - PTFF_R5_IS_GCREF = 0x0000000200000000, - PTFF_R5_IS_BYREF = 0x0000000400000000, + PTFF_THREAD_ABORT = 0x0000000080000000, // indicates that ThreadAbortException should be thrown when returning from the transition - PTFF_THREAD_ABORT = 0x0000000800000000, // indicates that ThreadAbortException should be thrown when returning from the transition + PTFF_THREAD_HIJACK = 0x0000000100000000, // indicates that this is a frame for a hijacked call }; #else // TARGET_ARM @@ -434,12 +425,14 @@ enum PInvokeTransitionFrameFlags PTFF_SAVE_R10 = 0x00002000, PTFF_SAVE_R11 = 0x00004000, +#if defined(TARGET_X86) PTFF_RAX_IS_GCREF = 0x00010000, // used by hijack handler to report return value of hijacked method - PTFF_RAX_IS_BYREF = 0x00020000, - PTFF_RDX_IS_GCREF = 0x00040000, - PTFF_RDX_IS_BYREF = 0x00080000, + PTFF_RAX_IS_BYREF = 0x00020000, +#endif PTFF_THREAD_ABORT = 0x00100000, // indicates that ThreadAbortException should be thrown when returning from the transition + + PTFF_THREAD_HIJACK = 0x00200000, // indicates that this is a frame for a hijacked call }; #endif // TARGET_ARM diff --git a/src/coreclr/nativeaot/Runtime/loongarch64/AsmOffsetsCpu.h b/src/coreclr/nativeaot/Runtime/loongarch64/AsmOffsetsCpu.h index cf6c7f5ae690a..0f48b5f227f8e 100644 --- a/src/coreclr/nativeaot/Runtime/loongarch64/AsmOffsetsCpu.h +++ b/src/coreclr/nativeaot/Runtime/loongarch64/AsmOffsetsCpu.h @@ -7,7 +7,7 @@ // // NOTE: the offsets MUST be in hex notation WITHOUT the 0x prefix -PLAT_ASM_SIZEOF(280, ExInfo) +PLAT_ASM_SIZEOF(270, ExInfo) PLAT_ASM_OFFSET(0, ExInfo, m_pPrevExInfo) PLAT_ASM_OFFSET(8, ExInfo, m_pExContext) PLAT_ASM_OFFSET(10, ExInfo, m_exception) @@ -15,7 +15,7 @@ PLAT_ASM_OFFSET(18, ExInfo, m_kind) PLAT_ASM_OFFSET(19, ExInfo, m_passNumber) PLAT_ASM_OFFSET(1c, ExInfo, m_idxCurClause) PLAT_ASM_OFFSET(20, ExInfo, m_frameIter) -PLAT_ASM_OFFSET(278, ExInfo, m_notifyDebuggerSP) +PLAT_ASM_OFFSET(268, ExInfo, m_notifyDebuggerSP) PLAT_ASM_OFFSET(0, PInvokeTransitionFrame, m_FramePointer) PLAT_ASM_OFFSET(8, PInvokeTransitionFrame, m_RIP) @@ -23,12 +23,12 @@ PLAT_ASM_OFFSET(10, PInvokeTransitionFrame, m_pThread) PLAT_ASM_OFFSET(18, PInvokeTransitionFrame, m_Flags) PLAT_ASM_OFFSET(20, PInvokeTransitionFrame, m_PreservedRegs) -PLAT_ASM_SIZEOF(258, StackFrameIterator) +PLAT_ASM_SIZEOF(248, StackFrameIterator) PLAT_ASM_OFFSET(10, StackFrameIterator, m_FramePointer) PLAT_ASM_OFFSET(18, StackFrameIterator, m_ControlPC) PLAT_ASM_OFFSET(20, StackFrameIterator, m_RegDisplay) -PLAT_ASM_OFFSET(248, StackFrameIterator, m_OriginalControlPC) -PLAT_ASM_OFFSET(250, StackFrameIterator, m_pPreviousTransitionFrame) +PLAT_ASM_OFFSET(238, StackFrameIterator, m_OriginalControlPC) +PLAT_ASM_OFFSET(240, StackFrameIterator, m_pPreviousTransitionFrame) PLAT_ASM_SIZEOF(B8, PAL_LIMITED_CONTEXT) diff --git a/src/coreclr/nativeaot/Runtime/loongarch64/GcProbe.S b/src/coreclr/nativeaot/Runtime/loongarch64/GcProbe.S index a581021fb5602..d5b7d7f352649 100644 --- a/src/coreclr/nativeaot/Runtime/loongarch64/GcProbe.S +++ b/src/coreclr/nativeaot/Runtime/loongarch64/GcProbe.S @@ -98,7 +98,6 @@ // // Register state on exit: // a2: thread pointer -// t3: transition frame flags for the return registers a0 and a1 // .macro FixupHijackedCallstack @@ -112,9 +111,8 @@ // // Fix the stack by restoring the original return address // - // Load m_pvHijackedReturnAddress and m_uHijackedReturnValueFlags + // Load m_pvHijackedReturnAddress ld.d $ra, $a2, OFFSETOF__Thread__m_pvHijackedReturnAddress - ld.d $t3, $a2, OFFSETOF__Thread__m_pvHijackedReturnAddress + 8 // // Clear hijack state @@ -122,9 +120,6 @@ // Clear m_ppvHijackedReturnAddressLocation and m_pvHijackedReturnAddress st.d $zero, $a2, OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation st.d $zero, $a2, OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation + 8 - // Clear m_uHijackedReturnValueFlags - st.d $zero, $a2, OFFSETOF__Thread__m_uHijackedReturnValueFlags - .endm // @@ -139,9 +134,8 @@ NESTED_ENTRY RhpGcProbeHijack, _TEXT, NoHandler jirl $r0, $ra, 0 LOCAL_LABEL(WaitForGC): - lu12i.w $t7, ((DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_R4 + PTFF_SAVE_R5) >> 12) & 0xfffff - ori $t7, $t7, (DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_R4 + PTFF_SAVE_R5) & 0xfff - or $t3, $t3, $t7 + lu12i.w $t3, ((DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_R4 + PTFF_SAVE_R5 + PTFF_THREAD_HIJACK) >> 12) & 0xfffff + ori $t3, $t3, (DEFAULT_FRAME_SAVE_FLAGS + PTFF_SAVE_R4 + PTFF_SAVE_R5 + PTFF_THREAD_HIJACK) & 0xfff b C_FUNC(RhpWaitForGC) NESTED_END RhpGcProbeHijack diff --git a/src/coreclr/nativeaot/Runtime/thread.cpp b/src/coreclr/nativeaot/Runtime/thread.cpp index 76beff6c96cf1..aad728782b18b 100644 --- a/src/coreclr/nativeaot/Runtime/thread.cpp +++ b/src/coreclr/nativeaot/Runtime/thread.cpp @@ -451,24 +451,16 @@ void Thread::GcScanRootsWorker(ScanFunc * pfnEnumCallback, ScanContext * pvCallb PTR_OBJECTREF pHijackedReturnValue = NULL; GCRefKind returnValueKind = GCRK_Unknown; +#ifdef TARGET_X86 if (frameIterator.GetHijackedReturnValueLocation(&pHijackedReturnValue, &returnValueKind)) { - GCRefKind reg0Kind = ExtractReg0ReturnKind(returnValueKind); - if (reg0Kind != GCRK_Scalar) - { - EnumGcRef(pHijackedReturnValue, reg0Kind, pfnEnumCallback, pvCallbackData); - } - -#if defined(TARGET_ARM64) || defined(TARGET_UNIX) - GCRefKind reg1Kind = ExtractReg1ReturnKind(returnValueKind); - if (reg1Kind != GCRK_Scalar) + GCRefKind returnKind = ExtractReturnKind(returnValueKind); + if (returnKind != GCRK_Scalar) { - // X0/X1 or RAX/RDX are saved in hijack frame next to each other in this order - EnumGcRef(pHijackedReturnValue + 1, reg1Kind, pfnEnumCallback, pvCallbackData); + EnumGcRef(pHijackedReturnValue, returnKind, pfnEnumCallback, pvCallbackData); } -#endif // TARGET_ARM64 || TARGET_UNIX - } +#endif #ifndef DACCESS_COMPILE if (GetRuntimeInstance()->IsConservativeStackReportingEnabled()) @@ -793,13 +785,11 @@ void Thread::HijackReturnAddress(NATIVE_CONTEXT* pSuspendCtx, HijackFunc* pfnHij void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, HijackFunc* pfnHijackFunction) { void** ppvRetAddrLocation; - GCRefKind retValueKind; frameIterator->CalculateCurrentMethodState(); if (frameIterator->GetCodeManager()->GetReturnAddressHijackInfo(frameIterator->GetMethodInfo(), frameIterator->GetRegisterSet(), - &ppvRetAddrLocation, - &retValueKind)) + &ppvRetAddrLocation)) { ASSERT(ppvRetAddrLocation != NULL); @@ -816,7 +806,12 @@ void Thread::HijackReturnAddressWorker(StackFrameIterator* frameIterator, Hijack m_ppvHijackedReturnAddressLocation = ppvRetAddrLocation; m_pvHijackedReturnAddress = pvRetAddr; - m_uHijackedReturnValueFlags = ReturnKindToTransitionFrameFlags(retValueKind); +#if defined(TARGET_X86) + m_uHijackedReturnValueFlags = ReturnKindToTransitionFrameFlags( + frameIterator->GetCodeManager()->GetReturnValueKind(frameIterator->GetMethodInfo(), + frameIterator->GetRegisterSet())); +#endif + *ppvRetAddrLocation = (void*)pfnHijackFunction; STRESS_LOG2(LF_STACKWALK, LL_INFO10000, "InternalHijack: TgtThread = %llx, IP = %p\n", @@ -950,7 +945,9 @@ void Thread::UnhijackWorker() // Clear the hijack state. m_ppvHijackedReturnAddressLocation = NULL; m_pvHijackedReturnAddress = NULL; +#ifdef TARGET_X86 m_uHijackedReturnValueFlags = 0; +#endif } bool Thread::IsHijacked() @@ -1362,4 +1359,4 @@ EXTERN_C void QCALLTYPE RhSetCurrentThreadName(const TCHAR* name) #else PalSetCurrentThreadName(name); #endif -} \ No newline at end of file +} diff --git a/src/coreclr/nativeaot/Runtime/thread.h b/src/coreclr/nativeaot/Runtime/thread.h index c75ec9a5c4a72..7252e20e84a3a 100644 --- a/src/coreclr/nativeaot/Runtime/thread.h +++ b/src/coreclr/nativeaot/Runtime/thread.h @@ -142,11 +142,11 @@ struct RuntimeThreadLocals #ifdef FEATURE_HIJACK void ** m_ppvHijackedReturnAddressLocation; void * m_pvHijackedReturnAddress; - uintptr_t m_uHijackedReturnValueFlags; #endif // FEATURE_HIJACK PTR_ExInfo m_pExInfoStackHead; Object* m_threadAbortException; // ThreadAbortException instance -set only during thread abort #ifdef TARGET_X86 + uintptr_t m_uHijackedReturnValueFlags; PCODE m_LastRedirectIP; uint64_t m_SpinCount; #endif diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp index 9f982f630bcec..7f1fdfecc5866 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.cpp @@ -1168,8 +1168,7 @@ GCRefKind GetGcRefKind(ReturnKind returnKind) bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet, // in - PTR_PTR_VOID * ppvRetAddrLocation, // out - GCRefKind * pRetValueKind) // out + PTR_PTR_VOID * ppvRetAddrLocation) // out { UnixNativeMethodInfo* pNativeMethodInfo = (UnixNativeMethodInfo*)pMethodInfo; @@ -1186,21 +1185,6 @@ bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn if ((unwindBlockFlags & UBF_FUNC_REVERSE_PINVOKE) != 0) return false; - if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) - p += sizeof(int32_t); - - if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) - p += sizeof(int32_t); - - // Decode the GC info for the current method to determine its return type - GcInfoDecoderFlags flags = DECODE_RETURN_KIND; -#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) - flags = (GcInfoDecoderFlags)(flags | DECODE_HAS_TAILCALLS); -#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64 - - GcInfoDecoder decoder(GCInfoToken(p), flags); - *pRetValueKind = GetGcRefKind(decoder.GetReturnKind()); - #if defined(TARGET_ARM) // Ensure that PC doesn't have the Thumb bit set. Prolog and epilog // checks depend on it. @@ -1247,8 +1231,17 @@ bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn *ppvRetAddrLocation = (PTR_PTR_VOID)(pRegisterSet->GetSP() - sizeof(TADDR)); return true; -#elif defined(TARGET_ARM64) || defined(TARGET_ARM) +#elif defined(TARGET_ARM64) || defined(TARGET_ARM) || defined(TARGET_LOONGARCH64) + + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) + p += sizeof(int32_t); + // Decode the GC info for the current method to determine if there are tailcalls + GcInfoDecoderFlags flags = DECODE_HAS_TAILCALLS; + GcInfoDecoder decoder(GCInfoToken(p), flags); if (decoder.HasTailCalls()) { // Do not hijack functions that have tail calls, since there are two problems: @@ -1263,6 +1256,7 @@ bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn return false; } +#ifndef TARGET_LOONGARCH64 PTR_uintptr_t pLR = pRegisterSet->pLR; if (!VirtualUnwind(pMethodInfo, pRegisterSet)) { @@ -1280,24 +1274,7 @@ bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn } *ppvRetAddrLocation = (PTR_PTR_VOID)pRegisterSet->pLR; - return true; - -#elif defined(TARGET_LOONGARCH64) - - if (decoder.HasTailCalls()) - { - // Do not hijack functions that have tail calls, since there are two problems: - // 1. When a function that tail calls another one is hijacked, the RA may be - // stored at a different location in the stack frame of the tail call target. - // So just by performing tail call, the hijacked location becomes invalid and - // unhijacking would corrupt stack by writing to that location. - // 2. There is a small window after the caller pops RA from the stack in its - // epilog and before the tail called function pushes RA in its prolog when - // the hijacked return address would not be not on the stack and so we would - // not be able to unhijack. - return false; - } - +#elif PTR_uintptr_t pRA = pRegisterSet->pRA; if (!VirtualUnwind(pMethodInfo, pRegisterSet)) { @@ -1315,6 +1292,8 @@ bool UnixNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn } *ppvRetAddrLocation = (PTR_PTR_VOID)pRegisterSet->pRA; +#endif // TARGET_LOONGARCH64 + return true; #else return false; diff --git a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.h b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.h index d7bfe94ac5ff4..ad9dd8cfed24c 100644 --- a/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.h +++ b/src/coreclr/nativeaot/Runtime/unix/UnixNativeCodeManager.h @@ -69,8 +69,7 @@ class UnixNativeCodeManager : public ICodeManager bool GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet, // in - PTR_PTR_VOID * ppvRetAddrLocation, // out - GCRefKind * pRetValueKind); // out + PTR_PTR_VOID * ppvRetAddrLocation); // out PTR_VOID RemapHardwareFaultToGCSafePoint(MethodInfo * pMethodInfo, PTR_VOID controlPC); diff --git a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosamd64.inc b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosamd64.inc index 05667a351a9d8..e4195ac8ac831 100644 --- a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosamd64.inc +++ b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosamd64.inc @@ -263,6 +263,7 @@ C_FUNC(\Name): #define PTFF_SAVE_RDX 0x00000400 // RDX is saved in hijack handler - in case it contains a GC ref #define PTFF_SAVE_ALL_SCRATCH 0x00007F00 #define PTFF_THREAD_ABORT 0x00100000 // indicates that ThreadAbortException should be thrown when returning from the transition +#define PTFF_THREAD_HIJACK 0x00200000 // indicates that this is a frame for a hijacked call // These must match the TrapThreadsFlags enum #define TrapThreadsFlags_None 0 diff --git a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc index 4ccd38b19c7be..8aeb084f8e3cd 100644 --- a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc +++ b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm.inc @@ -18,7 +18,8 @@ #define PTFF_SAVE_R9 0x00000020 #define PTFF_SAVE_SP 0x00000100 #define PTFF_SAVE_R0 0x00000200 -#define PTFF_THREAD_ABORT 0x00100000 +#define PTFF_THREAD_ABORT 0x00004000 +#define PTFF_THREAD_HIJACK 0x00008000 // indicates that this is a frame for a hijacked call #define DEFAULT_FRAME_SAVE_FLAGS (PTFF_SAVE_ALL_PRESERVED + PTFF_SAVE_SP) diff --git a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm64.inc b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm64.inc index a02a770aa3b0a..36698fece5050 100644 --- a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm64.inc +++ b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosarm64.inc @@ -306,6 +306,7 @@ C_FUNC(\Name): #define PTFF_SAVE_X0 0x00000800 #define PTFF_SAVE_X1 0x00001000 #define PTFF_SAVE_ALL_PRESERVED 0x000003FF // NOTE: x19-x28 +#define PTFF_THREAD_HIJACK_HI 0x00000002 // upper 32 bits of the PTFF_THREAD_HIJACK #define DEFAULT_FRAME_SAVE_FLAGS (PTFF_SAVE_ALL_PRESERVED + PTFF_SAVE_SP) @@ -345,7 +346,7 @@ C_FUNC(\Name): .endm // Bit position for the flags above, to be used with tbz / tbnz instructions -#define PTFF_THREAD_ABORT_BIT 36 +#define PTFF_THREAD_ABORT_BIT 32 // // CONSTANTS -- INTEGER diff --git a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosloongarch64.inc b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosloongarch64.inc index a035a3558cc63..f8b9d1aa63335 100644 --- a/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosloongarch64.inc +++ b/src/coreclr/nativeaot/Runtime/unix/unixasmmacrosloongarch64.inc @@ -309,7 +309,7 @@ C_FUNC(\Name): .endm // Bit position for the flags above, to be used with bstrpick.d+beq/bne instructions -#define PTFF_THREAD_ABORT_BIT 35 +#define PTFF_THREAD_ABORT_BIT 31 // // CONSTANTS -- INTEGER diff --git a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp index 66c775e0e2c05..5fe6e44670283 100644 --- a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp @@ -860,8 +860,7 @@ GCRefKind GetGcRefKind(ReturnKind returnKind) bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet, // in - PTR_PTR_VOID * ppvRetAddrLocation, // out - GCRefKind * pRetValueKind) // out + PTR_PTR_VOID * ppvRetAddrLocation) // out { CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo; @@ -872,9 +871,6 @@ bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn uint8_t unwindBlockFlags = *p++; - if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) - p += sizeof(int32_t); - // Check whether this is a funclet if ((unwindBlockFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT) return false; @@ -884,19 +880,7 @@ bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn if ((unwindBlockFlags & UBF_FUNC_REVERSE_PINVOKE) != 0) return false; - if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) - p += sizeof(int32_t); - #ifdef USE_GC_INFO_DECODER - // Decode the GC info for the current method to determine its return type - GcInfoDecoderFlags flags = DECODE_RETURN_KIND; -#if defined(TARGET_ARM64) - flags = (GcInfoDecoderFlags)(flags | DECODE_HAS_TAILCALLS); -#endif // TARGET_ARM64 - GcInfoDecoder decoder(GCInfoToken(p), flags); - - *pRetValueKind = GetGcRefKind(decoder.GetReturnKind()); - // Unwind the current method context to the caller's context to get its stack pointer // and obtain the location of the return address on the stack SIZE_T EstablisherFrame; @@ -924,6 +908,15 @@ bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn return true; #elif defined(TARGET_ARM64) + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) + p += sizeof(int32_t); + + // Decode the GC info for the current method to determine if there are tailcalls + GcInfoDecoderFlags flags = DECODE_HAS_TAILCALLS; + GcInfoDecoder decoder(GCInfoToken(p), flags); if (decoder.HasTailCalls()) { // Do not hijack functions that have tail calls, since there are two problems: @@ -1002,12 +995,22 @@ bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn } *ppvRetAddrLocation = (PTR_PTR_VOID)registerSet.PCTAddr; - *pRetValueKind = GetGcRefKind(infoBuf.returnKind); - return true; #endif } +#ifdef TARGET_X86 +GCRefKind CoffNativeCodeManager::GetReturnValueKind(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet) +{ + PTR_uint8_t gcInfo; + uint32_t codeOffset = GetCodeOffset(pMethodInfo, (PTR_VOID)pRegisterSet->IP, &gcInfo); + hdrInfo infoBuf; + size_t infoSize = DecodeGCHdrInfo(GCInfoToken(gcInfo), codeOffset, &infoBuf); + + return GetGcRefKind(infoBuf.returnKind); +} +#endif + PTR_VOID CoffNativeCodeManager::RemapHardwareFaultToGCSafePoint(MethodInfo * pMethodInfo, PTR_VOID controlPC) { // GCInfo decoder needs to know whether execution of the method is aborted diff --git a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h index c1dacbfd8f986..c85f525096779 100644 --- a/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h +++ b/src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h @@ -91,9 +91,13 @@ class CoffNativeCodeManager : public ICodeManager bool IsUnwindable(PTR_VOID pvAddress); bool GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, - REGDISPLAY * pRegisterSet, // in - PTR_PTR_VOID * ppvRetAddrLocation, // out - GCRefKind * pRetValueKind); // out + REGDISPLAY * pRegisterSet, // in + PTR_PTR_VOID * ppvRetAddrLocation); // out + +#ifdef TARGET_X86 + GCRefKind GetReturnValueKind(MethodInfo * pMethodInfo, + REGDISPLAY * pRegisterSet); +#endif PTR_VOID RemapHardwareFaultToGCSafePoint(MethodInfo * pMethodInfo, PTR_VOID controlPC); diff --git a/src/coreclr/vm/arm/stubs.cpp b/src/coreclr/vm/arm/stubs.cpp index 18ccde9888fb7..cef0bea5d6479 100644 --- a/src/coreclr/vm/arm/stubs.cpp +++ b/src/coreclr/vm/arm/stubs.cpp @@ -1676,6 +1676,7 @@ void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) pRD->pCurrentContext->Sp = PTR_TO_TADDR(m_Args) + sizeof(struct HijackArgs); pRD->pCurrentContext->R0 = m_Args->R0; + pRD->volatileCurrContextPointers.R0 = &m_Args->R0; pRD->pCurrentContext->R4 = m_Args->R4; pRD->pCurrentContext->R5 = m_Args->R5; diff --git a/src/coreclr/vm/arm64/stubs.cpp b/src/coreclr/vm/arm64/stubs.cpp index 6cf5a7ec84589..8f30ba580e39e 100644 --- a/src/coreclr/vm/arm64/stubs.cpp +++ b/src/coreclr/vm/arm64/stubs.cpp @@ -789,6 +789,10 @@ void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) pRD->pCurrentContext->Sp = PTR_TO_TADDR(m_Args) + s ; pRD->pCurrentContext->X0 = m_Args->X0; + pRD->pCurrentContext->X1 = m_Args->X1; + + pRD->volatileCurrContextPointers.X0 = &m_Args->X0; + pRD->volatileCurrContextPointers.X1 = &m_Args->X1; pRD->pCurrentContext->X19 = m_Args->X19; pRD->pCurrentContext->X20 = m_Args->X20; diff --git a/src/coreclr/vm/eetwain.cpp b/src/coreclr/vm/eetwain.cpp index c0655aa471c1c..25a4c73b65ea0 100644 --- a/src/coreclr/vm/eetwain.cpp +++ b/src/coreclr/vm/eetwain.cpp @@ -2122,7 +2122,7 @@ size_t EECodeManager::GetFunctionSize(GCInfoToken gcInfoToken) * returns true. * If hijacking is not possible for some reason, it return false. */ -bool EECodeManager::GetReturnAddressHijackInfo(GCInfoToken gcInfoToken, ReturnKind * returnKind) +bool EECodeManager::GetReturnAddressHijackInfo(GCInfoToken gcInfoToken X86_ARG(ReturnKind * returnKind)) { CONTRACTL{ NOTHROW; @@ -2145,7 +2145,7 @@ bool EECodeManager::GetReturnAddressHijackInfo(GCInfoToken gcInfoToken, ReturnKi return true; #else // !USE_GC_INFO_DECODER - GcInfoDecoder gcInfoDecoder(gcInfoToken, GcInfoDecoderFlags(DECODE_RETURN_KIND | DECODE_REVERSE_PINVOKE_VAR)); + GcInfoDecoder gcInfoDecoder(gcInfoToken, GcInfoDecoderFlags(DECODE_REVERSE_PINVOKE_VAR)); if (gcInfoDecoder.GetReversePInvokeFrameStackSlot() != NO_REVERSE_PINVOKE_FRAME) { @@ -2153,7 +2153,6 @@ bool EECodeManager::GetReturnAddressHijackInfo(GCInfoToken gcInfoToken, ReturnKi return false; } - *returnKind = gcInfoDecoder.GetReturnKind(); return true; #endif // USE_GC_INFO_DECODER } diff --git a/src/coreclr/vm/frames.cpp b/src/coreclr/vm/frames.cpp index e6afc8995ef8e..4b3fd5279303b 100644 --- a/src/coreclr/vm/frames.cpp +++ b/src/coreclr/vm/frames.cpp @@ -1202,7 +1202,7 @@ BOOL IsProtectedByGCFrame(OBJECTREF *ppObjectRef) #endif //!DACCESS_COMPILE #ifdef FEATURE_HIJACK - +#ifdef TARGET_X86 void HijackFrame::GcScanRoots(promote_func *fn, ScanContext* sc) { LIMITED_METHOD_CONTRACT; @@ -1220,9 +1220,7 @@ void HijackFrame::GcScanRoots(promote_func *fn, ScanContext* sc) switch (r) { -#ifdef TARGET_X86 case RT_Float: // Fall through -#endif case RT_Scalar: // nothing to report break; @@ -1248,7 +1246,7 @@ void HijackFrame::GcScanRoots(promote_func *fn, ScanContext* sc) regNo++; } while (moreRegisters); } - +#endif // TARGET_X86 #endif // FEATURE_HIJACK void ProtectByRefsFrame::GcScanRoots(promote_func *fn, ScanContext *sc) diff --git a/src/coreclr/vm/frames.h b/src/coreclr/vm/frames.h index 4ea6e33341d0a..8fb46b6041fa5 100644 --- a/src/coreclr/vm/frames.h +++ b/src/coreclr/vm/frames.h @@ -2097,7 +2097,22 @@ class HijackFrame : public Frame } virtual void UpdateRegDisplay(const PREGDISPLAY, bool updateFloats = false); + +#ifdef TARGET_X86 + // On x86 we need to specialcase return values virtual void GcScanRoots(promote_func *fn, ScanContext* sc); +#else + // On non-x86 platforms HijackFrame is just a more compact form of a resumable + // frame with main difference that OnHijackTripThread captures just the registers + // that can possibly contain GC roots. + // The regular reporting of a top frame will report everything that is live + // after the call as specified in GC info, thus we do not need to worry about + // return values. + virtual unsigned GetFrameAttribs() { + LIMITED_METHOD_DAC_CONTRACT; + return FRAME_ATTR_RESUMABLE; // Treat the next frame as the top frame. + } +#endif // HijackFrames are created by trip functions. See OnHijackTripThread() // They are real C++ objects on the stack. diff --git a/src/coreclr/vm/gcinfodecoder.cpp b/src/coreclr/vm/gcinfodecoder.cpp index 40c5c686c6583..4197e456d8263 100644 --- a/src/coreclr/vm/gcinfodecoder.cpp +++ b/src/coreclr/vm/gcinfodecoder.cpp @@ -91,9 +91,10 @@ bool GcInfoDecoder::PredecodeFatHeader(int remainingFlags) int numFlagBits = (m_Version == 1) ? GC_INFO_FLAGS_BIT_SIZE_VERSION_1 : GC_INFO_FLAGS_BIT_SIZE; m_headerFlags = (GcInfoHeaderFlags)m_Reader.Read(numFlagBits); - m_ReturnKind = (ReturnKind)((UINT32)m_Reader.Read(SIZE_OF_RETURN_KIND_IN_FAT_HEADER)); + // skip over the unused return kind. + m_Reader.Read(SIZE_OF_RETURN_KIND_IN_FAT_HEADER); - remainingFlags &= ~(DECODE_RETURN_KIND | DECODE_VARARG); + remainingFlags &= ~DECODE_VARARG; #if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) remainingFlags &= ~DECODE_HAS_TAILCALLS; #endif @@ -259,7 +260,6 @@ GcInfoDecoder::GcInfoDecoder( : m_Reader(dac_cast(gcInfoToken.Info)) , m_InstructionOffset(breakOffset) , m_IsInterruptible(false) - , m_ReturnKind(RT_Illegal) #ifdef _DEBUG , m_Flags( flags ) , m_GcInfoAddress(dac_cast(gcInfoToken.Info)) @@ -299,9 +299,10 @@ GcInfoDecoder::GcInfoDecoder( m_StackBaseRegister = NO_STACK_BASE_REGISTER; } - m_ReturnKind = (ReturnKind)((UINT32)m_Reader.Read(SIZE_OF_RETURN_KIND_IN_SLIM_HEADER)); + // skip over the unused return kind. + m_Reader.Read(SIZE_OF_RETURN_KIND_IN_SLIM_HEADER); - remainingFlags &= ~(DECODE_RETURN_KIND | DECODE_VARARG); + remainingFlags &= ~DECODE_VARARG; #if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) remainingFlags &= ~DECODE_HAS_TAILCALLS; #endif @@ -658,13 +659,6 @@ UINT32 GcInfoDecoder::GetCodeLength() return m_CodeLength; } -ReturnKind GcInfoDecoder::GetReturnKind() -{ - // SUPPORTS_DAC; - _ASSERTE( m_Flags & DECODE_RETURN_KIND ); - return m_ReturnKind; -} - UINT32 GcInfoDecoder::GetStackBaseRegister() { return m_StackBaseRegister; diff --git a/src/coreclr/vm/loongarch64/stubs.cpp b/src/coreclr/vm/loongarch64/stubs.cpp index fc047df10fbd4..a73727ee45b78 100644 --- a/src/coreclr/vm/loongarch64/stubs.cpp +++ b/src/coreclr/vm/loongarch64/stubs.cpp @@ -819,6 +819,10 @@ void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) pRD->pCurrentContext->Sp = PTR_TO_TADDR(m_Args) + s ; pRD->pCurrentContext->A0 = m_Args->A0; + pRD->pCurrentContext->A1 = m_Args->A1; + + pRD->volatileCurrContextPointers.A0 = &m_Args->A0; + pRD->volatileCurrContextPointers.A1 = &m_Args->A1; pRD->pCurrentContext->S0 = m_Args->S0; pRD->pCurrentContext->S1 = m_Args->S1; diff --git a/src/coreclr/vm/riscv64/stubs.cpp b/src/coreclr/vm/riscv64/stubs.cpp index c170b00438893..026a43c482190 100644 --- a/src/coreclr/vm/riscv64/stubs.cpp +++ b/src/coreclr/vm/riscv64/stubs.cpp @@ -739,6 +739,11 @@ void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats) pRD->pCurrentContext->Sp = PTR_TO_TADDR(m_Args) + s ; pRD->pCurrentContext->A0 = m_Args->A0; + pRD->pCurrentContext->A1 = m_Args->A1; + + pRD->volatileCurrContextPointers.A0 = &m_Args->A0; + pRD->volatileCurrContextPointers.A1 = &m_Args->A1; + pRD->pCurrentContext->S1 = m_Args->S1; pRD->pCurrentContext->S2 = m_Args->S2; diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index e89b6c7d94c1d..8951a68905a23 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -1498,7 +1498,10 @@ Thread::Thread() #ifdef FEATURE_PERFTRACING memset(&m_activityId, 0, sizeof(m_activityId)); #endif // FEATURE_PERFTRACING + +#ifdef TARGET_X86 m_HijackReturnKind = RT_Illegal; +#endif m_currentPrepareCodeConfig = nullptr; m_isInForbidSuspendForDebuggerRegion = false; diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index 2e125d4ddd78b..40b182b728ef7 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -2672,7 +2672,7 @@ class Thread private: #ifdef FEATURE_HIJACK - void HijackThread(ReturnKind returnKind, ExecutionState *esb); + void HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind)); VOID *m_pvHJRetAddr; // original return address (before hijack) VOID **m_ppvHJRetAddrPtr; // place we bashed a new return address @@ -3843,15 +3843,14 @@ class Thread #endif // FEATURE_PERFTRACING #ifdef FEATURE_HIJACK -private: +#ifdef TARGET_X86 +private: // By the time a frame is scanned by the runtime, m_pHijackReturnKind always // identifies the gc-ness of the return register(s) - ReturnKind m_HijackReturnKind; public: - ReturnKind GetHijackReturnKind() { LIMITED_METHOD_CONTRACT; @@ -3865,6 +3864,7 @@ class Thread m_HijackReturnKind = returnKind; } +#endif #endif // FEATURE_HIJACK public: diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp index 505ceb174684f..a8e17f902ab0e 100644 --- a/src/coreclr/vm/threadsuspend.cpp +++ b/src/coreclr/vm/threadsuspend.cpp @@ -4546,7 +4546,7 @@ struct ExecutionState }; // Client is responsible for suspending the thread before calling -void Thread::HijackThread(ReturnKind returnKind, ExecutionState *esb) +void Thread::HijackThread(ExecutionState *esb X86_ARG(ReturnKind returnKind)) { CONTRACTL { NOTHROW; @@ -4554,8 +4554,6 @@ void Thread::HijackThread(ReturnKind returnKind, ExecutionState *esb) } CONTRACTL_END; - _ASSERTE(IsValidReturnKind(returnKind)); - VOID *pvHijackAddr = reinterpret_cast(OnHijackTripThread); #if defined(TARGET_WINDOWS) @@ -4567,10 +4565,13 @@ void Thread::HijackThread(ReturnKind returnKind, ExecutionState *esb) #endif // TARGET_WINDOWS #ifdef TARGET_X86 + _ASSERTE(IsValidReturnKind(returnKind)); if (returnKind == RT_Float) { pvHijackAddr = reinterpret_cast(OnHijackFPTripThread); } + + SetHijackReturnKind(returnKind); #endif // TARGET_X86 // Don't hijack if are in the first level of running a filter/finally/catch. @@ -4590,8 +4591,6 @@ void Thread::HijackThread(ReturnKind returnKind, ExecutionState *esb) return; } - SetHijackReturnKind(returnKind); - if (m_State & TS_Hijacked) UnhijackThread(); @@ -4909,10 +4908,10 @@ void STDCALL OnHijackWorker(HijackArgs * pArgs) #endif // HIJACK_NONINTERRUPTIBLE_THREADS } -static bool GetReturnAddressHijackInfo(EECodeInfo *pCodeInfo, ReturnKind *pReturnKind) +static bool GetReturnAddressHijackInfo(EECodeInfo *pCodeInfo X86_ARG(ReturnKind * returnKind)) { GCInfoToken gcInfoToken = pCodeInfo->GetGCInfoToken(); - return pCodeInfo->GetCodeManager()->GetReturnAddressHijackInfo(gcInfoToken, pReturnKind); + return pCodeInfo->GetCodeManager()->GetReturnAddressHijackInfo(gcInfoToken X86_ARG(returnKind)); } #ifndef TARGET_UNIX @@ -5312,11 +5311,10 @@ BOOL Thread::HandledJITCase() // it or not. EECodeInfo codeInfo(ip); - ReturnKind returnKind; - - if (GetReturnAddressHijackInfo(&codeInfo, &returnKind)) + X86_ONLY(ReturnKind returnKind;) + if (GetReturnAddressHijackInfo(&codeInfo X86_ARG(&returnKind))) { - HijackThread(returnKind, &esb); + HijackThread(&esb X86_ARG(returnKind)); } } } @@ -5852,9 +5850,8 @@ void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext) if (executionState.m_ppvRetAddrPtr == NULL) return; - ReturnKind returnKind; - - if (!GetReturnAddressHijackInfo(&codeInfo, &returnKind)) + X86_ONLY(ReturnKind returnKind;) + if (!GetReturnAddressHijackInfo(&codeInfo X86_ARG(&returnKind))) { return; } @@ -5868,7 +5865,7 @@ void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext) StackWalkerWalkingThreadHolder threadStackWalking(pThread); // Hijack the return address to point to the appropriate routine based on the method's return type. - pThread->HijackThread(returnKind, &executionState); + pThread->HijackThread(&executionState X86_ARG(returnKind)); } } From 4695f3f0e2fcfdd94602a21d6ef0670fc85f6d2a Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Thu, 19 Dec 2024 06:45:48 +0000 Subject: [PATCH 18/25] Fix comments in `Latin1CharInfo` (#110829) --- .../System.Private.CoreLib/src/System/Char.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Char.cs b/src/libraries/System.Private.CoreLib/src/System/Char.cs index 1e718d9786349..d43b737a0c7cb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Char.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Char.cs @@ -59,21 +59,21 @@ public readonly struct Char [ // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x0E, 0x0E, // U+0000..U+000F - 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, // U+0001..U+001F - 0x8B, 0x18, 0x18, 0x18, 0x1A, 0x18, 0x18, 0x18, 0x14, 0x15, 0x18, 0x19, 0x18, 0x13, 0x18, 0x18, // U+0002..U+002F - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x18, 0x18, 0x19, 0x19, 0x19, 0x18, // U+0003..U+003F - 0x18, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, // U+0004..U+004F - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x14, 0x18, 0x15, 0x1B, 0x12, // U+0005..U+005F - 0x1B, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // U+0006..U+006F - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x14, 0x19, 0x15, 0x19, 0x0E, // U+0007..U+007F - 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x8E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, // U+0008..U+008F - 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, // U+0009..U+009F - 0x8B, 0x18, 0x1A, 0x1A, 0x1A, 0x1A, 0x1C, 0x18, 0x1B, 0x1C, 0x04, 0x16, 0x19, 0x0F, 0x1C, 0x1B, // U+000A..U+00AF - 0x1C, 0x19, 0x0A, 0x0A, 0x1B, 0x21, 0x18, 0x18, 0x1B, 0x0A, 0x04, 0x17, 0x0A, 0x0A, 0x0A, 0x18, // U+000B..U+00BF - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, // U+000C..U+00CF - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x19, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x21, // U+000D..U+00DF - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // U+000E..U+00EF - 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x19, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // U+000F..U+00FF + 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, // U+0010..U+001F + 0x8B, 0x18, 0x18, 0x18, 0x1A, 0x18, 0x18, 0x18, 0x14, 0x15, 0x18, 0x19, 0x18, 0x13, 0x18, 0x18, // U+0020..U+002F + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x18, 0x18, 0x19, 0x19, 0x19, 0x18, // U+0030..U+003F + 0x18, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, // U+0040..U+004F + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x14, 0x18, 0x15, 0x1B, 0x12, // U+0050..U+005F + 0x1B, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // U+0060..U+006F + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x14, 0x19, 0x15, 0x19, 0x0E, // U+0070..U+007F + 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x8E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, // U+0080..U+008F + 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, // U+0090..U+009F + 0x8B, 0x18, 0x1A, 0x1A, 0x1A, 0x1A, 0x1C, 0x18, 0x1B, 0x1C, 0x04, 0x16, 0x19, 0x0F, 0x1C, 0x1B, // U+00A0..U+00AF + 0x1C, 0x19, 0x0A, 0x0A, 0x1B, 0x21, 0x18, 0x18, 0x1B, 0x0A, 0x04, 0x17, 0x0A, 0x0A, 0x0A, 0x18, // U+00B0..U+00BF + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, // U+00C0..U+00CF + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x19, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x21, // U+00D0..U+00DF + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // U+00E0..U+00EF + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x19, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, // U+00F0..U+00FF ]; // Return true for all characters below or equal U+00ff, which is ASCII + Latin-1 Supplement. From 2d1175bb24a234c48d0a52e608ac8142ca102da6 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 12:49:51 +0100 Subject: [PATCH 19/25] [main] Update dependencies from dotnet/arcade (#110781) * Update dependencies from https://github.com/dotnet/arcade build 20241216.1 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.TargetFramework , Microsoft.DotNet.Build.Tasks.Templating , Microsoft.DotNet.Build.Tasks.Workloads , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.GenAPI , Microsoft.DotNet.GenFacades , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.SharedFramework.Sdk , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitAssert , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.XUnitExtensions From Version 10.0.0-beta.24613.2 -> To Version 10.0.0-beta.24616.1 * Update dependencies from https://github.com/dotnet/arcade build 20241217.2 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Build.Tasks.Archives , Microsoft.DotNet.Build.Tasks.Feed , Microsoft.DotNet.Build.Tasks.Installers , Microsoft.DotNet.Build.Tasks.Packaging , Microsoft.DotNet.Build.Tasks.TargetFramework , Microsoft.DotNet.Build.Tasks.Templating , Microsoft.DotNet.Build.Tasks.Workloads , Microsoft.DotNet.CodeAnalysis , Microsoft.DotNet.GenAPI , Microsoft.DotNet.GenFacades , Microsoft.DotNet.Helix.Sdk , Microsoft.DotNet.PackageTesting , Microsoft.DotNet.RemoteExecutor , Microsoft.DotNet.SharedFramework.Sdk , Microsoft.DotNet.VersionTools.Tasks , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitAssert , Microsoft.DotNet.XUnitConsoleRunner , Microsoft.DotNet.XUnitExtensions From Version 10.0.0-beta.24613.2 -> To Version 10.0.0-beta.24617.2 --------- Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 84 ++++++++++++++++---------------- eng/Versions.props | 32 ++++++------ eng/common/cross/build-rootfs.sh | 2 +- global.json | 6 +-- 4 files changed, 62 insertions(+), 62 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 03c6e3e30c66f..d9aa8b9ae2326 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -84,87 +84,87 @@ - + https://github.com/dotnet/arcade - 255d5e0c89958af276883a988108c2d616438805 + 4f2968fce08735a7e22fca6bd4c99d003221d716 - + https://github.com/dotnet/arcade - 255d5e0c89958af276883a988108c2d616438805 + 4f2968fce08735a7e22fca6bd4c99d003221d716 - + https://github.com/dotnet/arcade - 255d5e0c89958af276883a988108c2d616438805 + 4f2968fce08735a7e22fca6bd4c99d003221d716 - + https://github.com/dotnet/arcade - 255d5e0c89958af276883a988108c2d616438805 + 4f2968fce08735a7e22fca6bd4c99d003221d716 - + https://github.com/dotnet/arcade - 255d5e0c89958af276883a988108c2d616438805 + 4f2968fce08735a7e22fca6bd4c99d003221d716 - + https://github.com/dotnet/arcade - 255d5e0c89958af276883a988108c2d616438805 + 4f2968fce08735a7e22fca6bd4c99d003221d716 - + https://github.com/dotnet/arcade - 255d5e0c89958af276883a988108c2d616438805 + 4f2968fce08735a7e22fca6bd4c99d003221d716 - + https://github.com/dotnet/arcade - 255d5e0c89958af276883a988108c2d616438805 + 4f2968fce08735a7e22fca6bd4c99d003221d716 - + https://github.com/dotnet/arcade - 255d5e0c89958af276883a988108c2d616438805 + 4f2968fce08735a7e22fca6bd4c99d003221d716 - + https://github.com/dotnet/arcade - 255d5e0c89958af276883a988108c2d616438805 + 4f2968fce08735a7e22fca6bd4c99d003221d716 - + https://github.com/dotnet/arcade - 255d5e0c89958af276883a988108c2d616438805 + 4f2968fce08735a7e22fca6bd4c99d003221d716 - + https://github.com/dotnet/arcade - 255d5e0c89958af276883a988108c2d616438805 + 4f2968fce08735a7e22fca6bd4c99d003221d716 - + https://github.com/dotnet/arcade - 255d5e0c89958af276883a988108c2d616438805 + 4f2968fce08735a7e22fca6bd4c99d003221d716 - + https://github.com/dotnet/arcade - 255d5e0c89958af276883a988108c2d616438805 + 4f2968fce08735a7e22fca6bd4c99d003221d716 - + https://github.com/dotnet/arcade - 255d5e0c89958af276883a988108c2d616438805 + 4f2968fce08735a7e22fca6bd4c99d003221d716 - + https://github.com/dotnet/arcade - 255d5e0c89958af276883a988108c2d616438805 + 4f2968fce08735a7e22fca6bd4c99d003221d716 - + https://github.com/dotnet/arcade - 255d5e0c89958af276883a988108c2d616438805 + 4f2968fce08735a7e22fca6bd4c99d003221d716 - + https://github.com/dotnet/arcade - 255d5e0c89958af276883a988108c2d616438805 + 4f2968fce08735a7e22fca6bd4c99d003221d716 - + https://github.com/dotnet/arcade - 255d5e0c89958af276883a988108c2d616438805 + 4f2968fce08735a7e22fca6bd4c99d003221d716 - + https://github.com/dotnet/arcade - 255d5e0c89958af276883a988108c2d616438805 + 4f2968fce08735a7e22fca6bd4c99d003221d716 https://github.com/dotnet/runtime-assets @@ -348,9 +348,9 @@ https://github.com/dotnet/xharness 3119edb6d70fb252e6128b0c7e45d3fc2f49f249 - + https://github.com/dotnet/arcade - 255d5e0c89958af276883a988108c2d616438805 + 4f2968fce08735a7e22fca6bd4c99d003221d716 https://dev.azure.com/dnceng/internal/_git/dotnet-optimization diff --git a/eng/Versions.props b/eng/Versions.props index 6414fab95673d..b6f1450dcdb56 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -85,22 +85,22 @@ 10.0.100-alpha.1.24610.7 - 10.0.0-beta.24613.2 - 10.0.0-beta.24613.2 - 10.0.0-beta.24613.2 - 10.0.0-beta.24613.2 - 2.9.2-beta.24613.2 - 10.0.0-beta.24613.2 - 2.9.2-beta.24613.2 - 10.0.0-beta.24613.2 - 10.0.0-beta.24613.2 - 10.0.0-beta.24613.2 - 10.0.0-beta.24613.2 - 10.0.0-beta.24613.2 - 10.0.0-beta.24613.2 - 10.0.0-beta.24613.2 - 10.0.0-beta.24613.2 - 10.0.0-beta.24613.2 + 10.0.0-beta.24617.2 + 10.0.0-beta.24617.2 + 10.0.0-beta.24617.2 + 10.0.0-beta.24617.2 + 2.9.2-beta.24617.2 + 10.0.0-beta.24617.2 + 2.9.2-beta.24617.2 + 10.0.0-beta.24617.2 + 10.0.0-beta.24617.2 + 10.0.0-beta.24617.2 + 10.0.0-beta.24617.2 + 10.0.0-beta.24617.2 + 10.0.0-beta.24617.2 + 10.0.0-beta.24617.2 + 10.0.0-beta.24617.2 + 10.0.0-beta.24617.2 1.4.0 diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh index de9807297909d..464040aaaea6e 100755 --- a/eng/common/cross/build-rootfs.sh +++ b/eng/common/cross/build-rootfs.sh @@ -422,7 +422,7 @@ case "$__AlpineVersion" in elif [[ "$__AlpineArch" == "riscv64" ]]; then __AlpineLlvmLibsLookup=1 __AlpineVersion=edge # minimum version with APKINDEX.tar.gz (packages archive) - elif [[ -n "$__AlpineVersion" ]]; then + elif [[ -n "$__AlpineMajorVersion" ]]; then # use whichever alpine version is provided and select the latest toolchain libs __AlpineLlvmLibsLookup=1 else diff --git a/global.json b/global.json index 796b273c5d159..1bf48f7ddcc0b 100644 --- a/global.json +++ b/global.json @@ -8,9 +8,9 @@ "dotnet": "10.0.100-alpha.1.24610.7" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.24613.2", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.24613.2", - "Microsoft.DotNet.SharedFramework.Sdk": "10.0.0-beta.24613.2", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.24617.2", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.24617.2", + "Microsoft.DotNet.SharedFramework.Sdk": "10.0.0-beta.24617.2", "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.4.0", "Microsoft.NET.Sdk.IL": "10.0.0-alpha.1.24565.3" From 36fef72a01727b0c58b8d5aa613f1711e934619e Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Thu, 19 Dec 2024 08:59:52 -0600 Subject: [PATCH 20/25] Support generic fields in PersistedAssemblyBuilder (#110372) --- .../Reflection/Emit/ModuleBuilderImpl.cs | 15 ++-- .../AssemblySaveTypeBuilderTests.cs | 89 ++++++++++++++++++- 2 files changed, 95 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs index e773ab500959f..02663bfa182d8 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs @@ -734,13 +734,16 @@ private EntityHandle GetMemberReferenceHandle(MemberInfo memberInfo) { case FieldInfo field: Type declaringType = field.DeclaringType!; - if (field.DeclaringType!.IsGenericTypeDefinition) + if (declaringType.IsGenericTypeDefinition) { //The type of the field has to be fully instantiated type. declaringType = declaringType.MakeGenericType(declaringType.GetGenericArguments()); } + + Type fieldType = ((FieldInfo)GetOriginalMemberIfConstructedType(field)).FieldType; memberHandle = AddMemberReference(field.Name, GetTypeHandle(declaringType), - MetadataSignatureHelper.GetFieldSignature(field.FieldType, field.GetRequiredCustomModifiers(), field.GetOptionalCustomModifiers(), this)); + MetadataSignatureHelper.GetFieldSignature(fieldType, field.GetRequiredCustomModifiers(), field.GetOptionalCustomModifiers(), this)); + break; case ConstructorInfo ctor: ctor = (ConstructorInfo)GetOriginalMemberIfConstructedType(ctor); @@ -809,17 +812,17 @@ internal static SignatureCallingConvention GetSignatureConvention(CallingConvent return convention; } - private MemberInfo GetOriginalMemberIfConstructedType(MethodBase methodBase) + private MemberInfo GetOriginalMemberIfConstructedType(MemberInfo memberInfo) { - Type declaringType = methodBase.DeclaringType!; + Type declaringType = memberInfo.DeclaringType!; if (declaringType.IsConstructedGenericType && declaringType.GetGenericTypeDefinition() is not TypeBuilderImpl && !ContainsTypeBuilder(declaringType.GetGenericArguments())) { - return declaringType.GetGenericTypeDefinition().GetMemberWithSameMetadataDefinitionAs(methodBase); + return declaringType.GetGenericTypeDefinition().GetMemberWithSameMetadataDefinitionAs(memberInfo); } - return methodBase; + return memberInfo; } private static Type[] ParameterTypes(ParameterInfo[] parameterInfos) diff --git a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderTests.cs index 31536bc266c0a..f508da07364f2 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderTests.cs @@ -5,6 +5,8 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; using Xunit; namespace System.Reflection.Emit.Tests @@ -124,11 +126,15 @@ public void CreateMembersThatUsesTypeLoadedFromCoreAssemblyTest() } } - private static TypeBuilder CreateAssemblyAndDefineType(out PersistedAssemblyBuilder assemblyBuilder) + private static ModuleBuilder CreateAssembly(out PersistedAssemblyBuilder assemblyBuilder) { assemblyBuilder = AssemblySaveTools.PopulateAssemblyBuilder(s_assemblyName); - return assemblyBuilder.DefineDynamicModule("MyModule") - .DefineType("TestInterface", TypeAttributes.Interface | TypeAttributes.Abstract); + return assemblyBuilder.DefineDynamicModule("MyModule"); + } + + private static TypeBuilder CreateAssemblyAndDefineType(out PersistedAssemblyBuilder assemblyBuilder) + { + return CreateAssembly(out assemblyBuilder).DefineType("TestInterface", TypeAttributes.Interface | TypeAttributes.Abstract); } [Fact] @@ -205,6 +211,83 @@ public void SaveGenericTypeParametersForAType(string[] typeParamNames) } } + private class GenericClassWithGenericField + { +#pragma warning disable CS0649 + public T F; +#pragma warning restore CS0649 + } + + private class GenericClassWithNonGenericField + { +#pragma warning disable CS0649 + public int F; +#pragma warning restore CS0649 + } + + public static IEnumerable GenericTypesWithField() + { + yield return new object[] { typeof(GenericClassWithGenericField), true }; + yield return new object[] { typeof(GenericClassWithNonGenericField), false }; + } + + [Theory] + [MemberData(nameof(GenericTypesWithField))] + public void SaveGenericField(Type declaringType, bool shouldFieldBeGeneric) + { + using (TempFile file = TempFile.Create()) + { + ModuleBuilder mb = CreateAssembly(out PersistedAssemblyBuilder assemblyBuilder); + TypeBuilder tb = mb.DefineType("C", TypeAttributes.Class); + MethodBuilder method = tb.DefineMethod("TestMethod", MethodAttributes.Public, returnType: typeof(int), parameterTypes: null); + ILGenerator il = method.GetILGenerator(); + il.Emit(OpCodes.Newobj, declaringType.GetConstructor([])); + il.Emit(OpCodes.Ldfld, declaringType.GetField("F")); + il.Emit(OpCodes.Ret); + Type createdType = tb.CreateType(); + assemblyBuilder.Save(file.Path); + + using (FileStream stream = File.OpenRead(file.Path)) + { + using (PEReader peReader = new PEReader(stream)) + { + bool found = false; + MetadataReader metadataReader = peReader.GetMetadataReader(); + foreach (MemberReferenceHandle memberRefHandle in metadataReader.MemberReferences) + { + MemberReference memberRef = metadataReader.GetMemberReference(memberRefHandle); + if (memberRef.GetKind() == MemberReferenceKind.Field) + { + Assert.False(found); + found = true; + + Assert.Equal("F", metadataReader.GetString(memberRef.Name)); + + // A reference to a generic field should point to the open generic field, and not the resolved generic type. + Assert.Equal(shouldFieldBeGeneric, IsGenericField(metadataReader.GetBlobReader(memberRef.Signature))); + } + } + + Assert.True(found); + } + } + } + + static bool IsGenericField(BlobReader signatureReader) + { + while (signatureReader.RemainingBytes > 0) + { + SignatureTypeCode typeCode = signatureReader.ReadSignatureTypeCode(); + if (typeCode == SignatureTypeCode.GenericTypeParameter) + { + return true; + } + } + + return false; + } + } + private static void SetVariousGenericParameterValues(GenericTypeParameterBuilder[] typeParams) { typeParams[0].SetInterfaceConstraints([typeof(IAccess), typeof(INoMethod)]); From b91087f9234bdc529d23dd2905f9813248615c3c Mon Sep 17 00:00:00 2001 From: Johan Lorensson Date: Thu, 19 Dec 2024 17:28:50 +0100 Subject: [PATCH 21/25] Fix NativeAOT ThunksPool thunk data block size handling. (#110732) * Fix NativeAOT ThunksPool thunk data block size handling. dotnet/runtime#88710 made a change in TunkPool.cs moving away from using a page size define to calling ThunkBlockSize to get to end of thunk data block where a common stub address get stored. This change is not equivalent on platforms where the thunk blocks are laid out in pair where a stub thunk blocks are followed by a data thunk block and all gets mapped from file. This is the schema used on Windows platforms. In that layout schema the ThunkBlockSize is 2 * page size meaning that the calculation getting to the end of the thunk data block will move to the end of next thunk stub that is RX memory and storing the common stub address at that location will trigger an AV. This works on iOS since it reports its ThunkBlockSize as one page but that is not totally correct since it uses 2 pages, just that they are allocated in the same way as FEATURE_RX_THUNKS, all thunk stubs blocks followed by all thunk data blocks. The reason why this works is because it only maps the thunk stubs from file, reporting a ThunkBlockSize that is inline with what gets map:ed from file, but then there is a special handling in PalAllocateThunksFromTemplate on iOS that virutal alloc template size * 2, mapping the first template size bytes from the file and the rest are kept as its thunk data blocks. This commit adds a new function returning the size of the thunk data block and use that when calculating the end of the data block instead of using the ThunkBlockSize since its reflects the size of each block getting mapped from file, on Windows platforms that is stub+data, 2 * page size. * Switch to ThunkDataBlockSizeMask in one place. * Include pointer size slot in data block size calculation. Co-authored-by: Jan Kotas --- .../src/System/Runtime/ThunkPool.cs | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/ThunkPool.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/ThunkPool.cs index 5996a37b81a54..82014594933ed 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/ThunkPool.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/ThunkPool.cs @@ -35,6 +35,7 @@ // using System.Diagnostics; +using System.Numerics; namespace System.Runtime { @@ -44,8 +45,10 @@ internal static class Constants public static readonly int ThunkCodeSize = RuntimeImports.RhpGetThunkSize(); public static readonly int NumThunksPerBlock = RuntimeImports.RhpGetNumThunksPerBlock(); public static readonly int NumThunkBlocksPerMapping = RuntimeImports.RhpGetNumThunkBlocksPerMapping(); - public static readonly uint ThunkBlockSize = (uint)RuntimeImports.RhpGetThunkBlockSize(); - public static readonly nuint ThunkBlockSizeMask = ThunkBlockSize - 1; + public static readonly uint ThunkCodeBlockSize = BitOperations.RoundUpToPowerOf2((uint)(ThunkCodeSize * NumThunksPerBlock)); + public static readonly nuint ThunkCodeBlockSizeMask = ThunkCodeBlockSize - 1; + public static readonly uint ThunkDataBlockSize = BitOperations.RoundUpToPowerOf2((uint)(ThunkDataSize * NumThunksPerBlock + IntPtr.Size)); + public static readonly nuint ThunkDataBlockSizeMask = ThunkDataBlockSize - 1; } internal class ThunksHeap @@ -97,11 +100,11 @@ private unsafe ThunksHeap(IntPtr commonStubAddress) IntPtr thunkDataBlock = RuntimeImports.RhpGetThunkDataBlockAddress(thunkStubsBlock); // Address of the first thunk data cell should be at the beginning of the thunks data block (page-aligned) - Debug.Assert(((nuint)(nint)thunkDataBlock % Constants.ThunkBlockSize) == 0); + Debug.Assert(((nuint)(nint)thunkDataBlock % Constants.ThunkDataBlockSize) == 0); // Update the last pointer value in the thunks data section with the value of the common stub address - *(IntPtr*)(thunkDataBlock + (int)(Constants.ThunkBlockSize - IntPtr.Size)) = commonStubAddress; - Debug.Assert(*(IntPtr*)(thunkDataBlock + (int)(Constants.ThunkBlockSize - IntPtr.Size)) == commonStubAddress); + *(IntPtr*)(thunkDataBlock + (int)(Constants.ThunkDataBlockSize - IntPtr.Size)) = commonStubAddress; + Debug.Assert(*(IntPtr*)(thunkDataBlock + (int)(Constants.ThunkDataBlockSize - IntPtr.Size)) == commonStubAddress); // Set the head and end of the linked list _nextAvailableThunkPtr = thunkDataBlock; @@ -153,11 +156,11 @@ private unsafe bool ExpandHeap() IntPtr thunkDataBlock = RuntimeImports.RhpGetThunkDataBlockAddress(thunkStubsBlock); // Address of the first thunk data cell should be at the beginning of the thunks data block (page-aligned) - Debug.Assert(((nuint)(nint)thunkDataBlock % Constants.ThunkBlockSize) == 0); + Debug.Assert(((nuint)(nint)thunkDataBlock % Constants.ThunkDataBlockSize) == 0); // Update the last pointer value in the thunks data section with the value of the common stub address - *(IntPtr*)(thunkDataBlock + (int)(Constants.ThunkBlockSize - IntPtr.Size)) = _commonStubAddress; - Debug.Assert(*(IntPtr*)(thunkDataBlock + (int)(Constants.ThunkBlockSize - IntPtr.Size)) == _commonStubAddress); + *(IntPtr*)(thunkDataBlock + (int)(Constants.ThunkDataBlockSize - IntPtr.Size)) = _commonStubAddress; + Debug.Assert(*(IntPtr*)(thunkDataBlock + (int)(Constants.ThunkDataBlockSize - IntPtr.Size)) == _commonStubAddress); // Link the last entry in the old list to the first entry in the new list *((IntPtr*)_lastThunkPtr) = thunkDataBlock; @@ -210,7 +213,7 @@ public unsafe IntPtr AllocateThunk() *((IntPtr*)(nextAvailableThunkPtr + IntPtr.Size)) = IntPtr.Zero; #endif - int thunkIndex = (int)(((nuint)(nint)nextAvailableThunkPtr) - ((nuint)(nint)nextAvailableThunkPtr & ~Constants.ThunkBlockSizeMask)); + int thunkIndex = (int)(((nuint)(nint)nextAvailableThunkPtr) - ((nuint)(nint)nextAvailableThunkPtr & ~Constants.ThunkDataBlockSizeMask)); Debug.Assert((thunkIndex % Constants.ThunkDataSize) == 0); thunkIndex /= Constants.ThunkDataSize; @@ -266,7 +269,7 @@ private static IntPtr TryGetThunkDataAddress(IntPtr thunkAddress) nuint thunkAddressValue = (nuint)(nint)ClearThumbBit(thunkAddress); // Compute the base address of the thunk's mapping - nuint currentThunksBlockAddress = thunkAddressValue & ~Constants.ThunkBlockSizeMask; + nuint currentThunksBlockAddress = thunkAddressValue & ~Constants.ThunkCodeBlockSizeMask; // Make sure the thunk address is valid by checking alignment if ((thunkAddressValue - currentThunksBlockAddress) % (nuint)Constants.ThunkCodeSize != 0) From 1019d315632c78b6bfc870e5893fa7ceb90b0e3c Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Thu, 19 Dec 2024 18:05:30 +0100 Subject: [PATCH 22/25] Remove unsafe code from IPAddress (#110824) --- .../src/System/Net/IPAddress.cs | 56 ++++++++----------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/src/libraries/System.Net.Primitives/src/System/Net/IPAddress.cs b/src/libraries/System.Net.Primitives/src/System/Net/IPAddress.cs index 6d2d6b75a8235..cd7e281e541c5 100644 --- a/src/libraries/System.Net.Primitives/src/System/Net/IPAddress.cs +++ b/src/libraries/System.Net.Primitives/src/System/Net/IPAddress.cs @@ -201,15 +201,12 @@ public IPAddress(ReadOnlySpan address) private static ushort[] ReadUInt16NumbersFromBytes(ReadOnlySpan address) { ushort[] numbers = new ushort[NumberOfLabels]; - if (Vector128.IsHardwareAccelerated) + if (Vector128.IsHardwareAccelerated && BitConverter.IsLittleEndian) { - Vector128 ushorts = Vector128.LoadUnsafe(ref MemoryMarshal.GetReference(address)).AsUInt16(); - if (BitConverter.IsLittleEndian) - { - // Reverse endianness of each ushort - ushorts = Vector128.ShiftLeft(ushorts, 8) | Vector128.ShiftRightLogical(ushorts, 8); - } - ushorts.StoreUnsafe(ref MemoryMarshal.GetArrayDataReference(numbers)); + Vector128 ushorts = Vector128.Create(address).AsUInt16(); + // Reverse endianness of each ushort + ushorts = (ushorts << 8) | (ushorts >> 8); + ushorts.CopyTo(numbers); } else { @@ -347,15 +344,15 @@ public bool TryWriteBytes(Span destination, out int bytesWritten) private void WriteIPv6Bytes(Span destination) { ushort[]? numbers = _numbers; - Debug.Assert(numbers != null && numbers.Length == NumberOfLabels); + Debug.Assert(numbers is { Length: NumberOfLabels }); if (BitConverter.IsLittleEndian) { if (Vector128.IsHardwareAccelerated) { - Vector128 ushorts = Vector128.LoadUnsafe(ref MemoryMarshal.GetArrayDataReference(numbers)); - ushorts = Vector128.ShiftLeft(ushorts, 8) | Vector128.ShiftRightLogical(ushorts, 8); - ushorts.AsByte().StoreUnsafe(ref MemoryMarshal.GetReference(destination)); + Vector128 ushorts = Vector128.Create(numbers).AsUInt16(); + ushorts = (ushorts << 8) | (ushorts >> 8); + ushorts.AsByte().CopyTo(destination); } else { @@ -387,7 +384,7 @@ public byte[] GetAddressBytes() { if (IsIPv6) { - Debug.Assert(_numbers != null && _numbers.Length == NumberOfLabels); + Debug.Assert(_numbers is { Length: NumberOfLabels }); byte[] bytes = new byte[IPAddressParserStatics.IPv6AddressBytes]; WriteIPv6Bytes(bytes); return bytes; @@ -633,15 +630,7 @@ public bool IsIPv4MappedToIPv6 { get { - if (IsIPv4) - { - return false; - } - - ReadOnlySpan numbers = MemoryMarshal.AsBytes(new ReadOnlySpan(_numbers)); - return - MemoryMarshal.Read(numbers) == 0 && - BinaryPrimitives.ReadUInt32LittleEndian(numbers.Slice(8)) == 0xFFFF0000; + return !IsIPv4 && _numbers.AsSpan(0, 6).SequenceEqual((ReadOnlySpan)[0, 0, 0, 0, 0, 0xFFFF]); } } @@ -695,18 +684,19 @@ internal bool Equals(IPAddress comparand) if (IsIPv6) { // For IPv6 addresses, we must compare the full 128-bit representation and the scope IDs. - ReadOnlySpan thisNumbers = MemoryMarshal.AsBytes(_numbers); - ReadOnlySpan comparandNumbers = MemoryMarshal.AsBytes(comparand._numbers); - return - MemoryMarshal.Read(thisNumbers) == MemoryMarshal.Read(comparandNumbers) && - MemoryMarshal.Read(thisNumbers.Slice(sizeof(ulong))) == MemoryMarshal.Read(comparandNumbers.Slice(sizeof(ulong))) && - PrivateScopeId == comparand.PrivateScopeId; - } - else - { - // For IPv4 addresses, compare the integer representation. - return comparand.PrivateAddress == PrivateAddress; + // We give JIT a hint that the arrays are always of length IPv6AddressShorts, so it + // can unroll the comparison. JIT probably could do it on its own, but that requires + // complex inter-procedural optimizations. + Debug.Assert(_numbers.Length == IPAddressParserStatics.IPv6AddressShorts); + Debug.Assert(comparand._numbers!.Length == IPAddressParserStatics.IPv6AddressShorts); + + ReadOnlySpan left = _numbers.AsSpan(0, IPAddressParserStatics.IPv6AddressShorts); + ReadOnlySpan right = comparand._numbers.AsSpan(0, IPAddressParserStatics.IPv6AddressShorts); + return left.SequenceEqual(right) && PrivateScopeId == comparand.PrivateScopeId; } + + // For IPv4 addresses, compare the integer representation. + return comparand.PrivateAddress == PrivateAddress; } public override int GetHashCode() From 208b974f93c0ee35a171eba374ca36aa9a78e930 Mon Sep 17 00:00:00 2001 From: Alex Covington <68252706+alexcovington@users.noreply.github.com> Date: Thu, 19 Dec 2024 13:15:10 -0800 Subject: [PATCH 23/25] Add vector support to System.Numerics.Tensors.TensorPrimitives.LeadingZeroCount for Byte and Int16 (#110333) * Working for byte * Working for ushort * Cleanup * Formatting * Update src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.LeadingZeroCount.cs Co-authored-by: Tanner Gooding * Add comment for usage of PermuteVar64x8x2 --------- Co-authored-by: Alex Covington (Advanced Micro Devices Co-authored-by: Tanner Gooding --- .../TensorPrimitives.LeadingZeroCount.cs | 119 ++++++++++++++++-- 1 file changed, 112 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.LeadingZeroCount.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.LeadingZeroCount.cs index 11018124a5992..0bc08e97166c2 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.LeadingZeroCount.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.LeadingZeroCount.cs @@ -29,7 +29,8 @@ public static void LeadingZeroCount(ReadOnlySpan x, Span destination) internal readonly unsafe struct LeadingZeroCountOperator : IUnaryOperator where T : IBinaryInteger { public static bool Vectorizable => - (Avx512CD.VL.IsSupported && (sizeof(T) == 4 || sizeof(T) == 8)) || + (Avx512CD.VL.IsSupported && (sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8)) || + (Avx512Vbmi.VL.IsSupported && sizeof(T) == 1) || (AdvSimd.IsSupported && (sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4)); public static T Invoke(T x) => T.LeadingZeroCount(x); @@ -37,10 +38,43 @@ public static void LeadingZeroCount(ReadOnlySpan x, Span destination) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Invoke(Vector128 x) { + if (Avx512Vbmi.VL.IsSupported && sizeof(T) == 1) + { + Vector128 lookupVectorLow = Vector128.Create((byte)8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4); + Vector128 lookupVectorHigh = Vector128.Create((byte)3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0); + Vector128 nibbleMask = Vector128.Create(0xF); + Vector128 permuteMask = Vector128.Create(0x80); + Vector128 lowNibble = x.AsByte() & nibbleMask; + Vector128 highNibble = Sse2.ShiftRightLogical(x.AsInt32(), 4).AsByte() & nibbleMask; + Vector128 nibbleSelectMask = Sse2.CompareEqual(highNibble, Vector128.Zero); + Vector128 indexVector = Sse41.BlendVariable(highNibble, lowNibble, nibbleSelectMask) + + (~nibbleSelectMask & nibbleMask); + indexVector |= ~nibbleSelectMask & permuteMask; + return Avx512Vbmi.VL.PermuteVar16x8x2(lookupVectorLow, indexVector, lookupVectorHigh).As(); + } + if (Avx512CD.VL.IsSupported) { - if (sizeof(T) == 4) return Avx512CD.VL.LeadingZeroCount(x.AsUInt32()).As(); - if (sizeof(T) == 8) return Avx512CD.VL.LeadingZeroCount(x.AsUInt64()).As(); + if (sizeof(T) == 2) + { + Vector128 lowHalf = Vector128.Create((uint)0x0000FFFF); + Vector128 x_bot16 = Sse2.Or(Sse2.ShiftLeftLogical(x.AsUInt32(), 16), lowHalf); + Vector128 x_top16 = Sse2.Or(x.AsUInt32(), lowHalf); + Vector128 lz_bot16 = Avx512CD.VL.LeadingZeroCount(x_bot16); + Vector128 lz_top16 = Avx512CD.VL.LeadingZeroCount(x_top16); + Vector128 lz_top16_shift = Sse2.ShiftLeftLogical(lz_top16, 16); + return Sse2.Or(lz_bot16, lz_top16_shift).AsUInt16().As(); + } + + if (sizeof(T) == 4) + { + return Avx512CD.VL.LeadingZeroCount(x.AsUInt32()).As(); + } + + if (sizeof(T) == 8) + { + return Avx512CD.VL.LeadingZeroCount(x.AsUInt64()).As(); + } } Debug.Assert(AdvSimd.IsSupported); @@ -56,10 +90,42 @@ public static Vector128 Invoke(Vector128 x) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Invoke(Vector256 x) { + if (Avx512Vbmi.VL.IsSupported && sizeof(T) == 1) + { + Vector256 lookupVector = + Vector256.Create((byte)8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0); + Vector256 nibbleMask = Vector256.Create(0xF); + Vector256 lowNibble = x.AsByte() & nibbleMask; + Vector256 highNibble = Avx2.ShiftRightLogical(x.AsInt32(), 4).AsByte() & nibbleMask; + Vector256 nibbleSelectMask = Avx2.CompareEqual(highNibble, Vector256.Zero); + Vector256 indexVector = Avx2.BlendVariable(highNibble, lowNibble, nibbleSelectMask) + + (~nibbleSelectMask & nibbleMask); + return Avx512Vbmi.VL.PermuteVar32x8(lookupVector, indexVector).As(); + } + if (Avx512CD.VL.IsSupported) { - if (sizeof(T) == 4) return Avx512CD.VL.LeadingZeroCount(x.AsUInt32()).As(); - if (sizeof(T) == 8) return Avx512CD.VL.LeadingZeroCount(x.AsUInt64()).As(); + if (sizeof(T) == 2) + { + Vector256 lowHalf = Vector256.Create((uint)0x0000FFFF); + Vector256 x_bot16 = Avx2.Or(Avx2.ShiftLeftLogical(x.AsUInt32(), 16), lowHalf); + Vector256 x_top16 = Avx2.Or(x.AsUInt32(), lowHalf); + Vector256 lz_bot16 = Avx512CD.VL.LeadingZeroCount(x_bot16); + Vector256 lz_top16 = Avx512CD.VL.LeadingZeroCount(x_top16); + Vector256 lz_top16_shift = Avx2.ShiftLeftLogical(lz_top16, 16); + return Avx2.Or(lz_bot16, lz_top16_shift).AsUInt16().As(); + } + + if (sizeof(T) == 4) + { + return Avx512CD.VL.LeadingZeroCount(x.AsUInt32()).As(); + } + + if (sizeof(T) == 8) + { + return Avx512CD.VL.LeadingZeroCount(x.AsUInt64()).As(); + } } return Vector256.Create(Invoke(x.GetLower()), Invoke(x.GetUpper())); @@ -68,10 +134,49 @@ public static Vector256 Invoke(Vector256 x) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Invoke(Vector512 x) { + if (Avx512BW.IsSupported && Avx512Vbmi.IsSupported && sizeof(T) == 1) + { + // Use each element of x as an index into a lookup table. + // Lookup can be broken down into the following: + // Bit 7 is set -- Result is 0, else result is from lookup table + // Bit 6 is set -- Use lookupVectorB, else use lookupVectorA + // Bit 5:0 -- Index to use for lookup table + Vector512 lookupVectorA = + Vector512.Create((byte)8, 7, 6, 6, 5, 5, 5, 5, + 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2); + Vector512 lookupVectorB = Vector512.Create((byte)1); + Vector512 bit7ZeroMask = Avx512BW.CompareLessThan(x.AsByte(), Vector512.Create((byte)128)); + return Avx512F.And(bit7ZeroMask, Avx512Vbmi.PermuteVar64x8x2(lookupVectorA, x.AsByte(), lookupVectorB)).As(); + } + if (Avx512CD.IsSupported) { - if (sizeof(T) == 4) return Avx512CD.LeadingZeroCount(x.AsUInt32()).As(); - if (sizeof(T) == 8) return Avx512CD.LeadingZeroCount(x.AsUInt64()).As(); + if (sizeof(T) == 2) + { + Vector512 lowHalf = Vector512.Create((uint)0x0000FFFF); + Vector512 x_bot16 = Avx512F.Or(Avx512F.ShiftLeftLogical(x.AsUInt32(), 16), lowHalf); + Vector512 x_top16 = Avx512F.Or(x.AsUInt32(), lowHalf); + Vector512 lz_bot16 = Avx512CD.LeadingZeroCount(x_bot16); + Vector512 lz_top16 = Avx512CD.LeadingZeroCount(x_top16); + Vector512 lz_top16_shift = Avx512F.ShiftLeftLogical(lz_top16, 16); + return Avx512F.Or(lz_bot16, lz_top16_shift).AsUInt16().As(); + } + + if (sizeof(T) == 4) + { + return Avx512CD.LeadingZeroCount(x.AsUInt32()).As(); + } + + if (sizeof(T) == 8) + { + return Avx512CD.LeadingZeroCount(x.AsUInt64()).As(); + } } return Vector512.Create(Invoke(x.GetLower()), Invoke(x.GetUpper())); From cf4d2b0594a7af4a5ff39ffd4fe5cfb0c5577a3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Fri, 20 Dec 2024 05:45:08 +0100 Subject: [PATCH 24/25] Fix OSEncoding math overflow (#110796) Discovered by @GrabYourPitchforks --- src/libraries/Common/src/System/Text/OSEncoding.Windows.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Common/src/System/Text/OSEncoding.Windows.cs b/src/libraries/Common/src/System/Text/OSEncoding.Windows.cs index fc3f866ec96a6..4d3d91e423e45 100644 --- a/src/libraries/Common/src/System/Text/OSEncoding.Windows.cs +++ b/src/libraries/Common/src/System/Text/OSEncoding.Windows.cs @@ -168,7 +168,7 @@ public override int GetMaxCharCount(int byteCount) { ArgumentOutOfRangeException.ThrowIfNegative(byteCount); - long charCount = byteCount * 4; // Max possible value for all encodings + long charCount = (long)byteCount * 4; // Max possible value for all encodings if (charCount > 0x7fffffff) throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_GetCharCountOverflow); From 64f5b0a2643bf63b8b13cd2ed01b58d5629aa812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 20 Dec 2024 09:37:43 +0100 Subject: [PATCH 25/25] Don't let System.CommandLine swallow exceptions (#110851) Apparently System.CommandLine will hijack unhandled exceptions by default so that it can print the stack trace in red. The price to pay for red stack traces is no crash dumps. It's not a very good tradeoff. --- src/coreclr/tools/ILVerify/Program.cs | 3 ++- src/coreclr/tools/aot/ILCompiler/Program.cs | 3 ++- src/coreclr/tools/aot/crossgen2/Program.cs | 3 ++- src/coreclr/tools/dotnet-pgo/Program.cs | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/coreclr/tools/ILVerify/Program.cs b/src/coreclr/tools/ILVerify/Program.cs index 88da48f50fb04..7dd39082a0c0e 100644 --- a/src/coreclr/tools/ILVerify/Program.cs +++ b/src/coreclr/tools/ILVerify/Program.cs @@ -481,7 +481,8 @@ public PEReader Resolve(string simpleName) private static int Main(string[] args) => new CliConfiguration(new ILVerifyRootCommand().UseVersion()) { - ResponseFileTokenReplacer = Helpers.TryReadResponseFile + ResponseFileTokenReplacer = Helpers.TryReadResponseFile, + EnableDefaultExceptionHandler = false, }.Invoke(args); } } diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index ae283029a8b1f..e9e7daa9691ba 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -791,7 +791,8 @@ private static int Main(string[] args) => .UseVersion() .UseExtendedHelp(ILCompilerRootCommand.GetExtendedHelp)) { - ResponseFileTokenReplacer = Helpers.TryReadResponseFile + ResponseFileTokenReplacer = Helpers.TryReadResponseFile, + EnableDefaultExceptionHandler = false, }.Invoke(args); } } diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index 37a5efaa7649c..1453a6cf17752 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -915,7 +915,8 @@ private static int Main(string[] args) => .UseVersion() .UseExtendedHelp(Crossgen2RootCommand.GetExtendedHelp)) { - ResponseFileTokenReplacer = Helpers.TryReadResponseFile + ResponseFileTokenReplacer = Helpers.TryReadResponseFile, + EnableDefaultExceptionHandler = false, }.Invoke(args); } } diff --git a/src/coreclr/tools/dotnet-pgo/Program.cs b/src/coreclr/tools/dotnet-pgo/Program.cs index af0765d4c0acb..c4745a74a2048 100644 --- a/src/coreclr/tools/dotnet-pgo/Program.cs +++ b/src/coreclr/tools/dotnet-pgo/Program.cs @@ -165,7 +165,8 @@ private static int Main(string[] args) => .UseVersion() .UseExtendedHelp(PgoRootCommand.GetExtendedHelp)) { - ResponseFileTokenReplacer = Helpers.TryReadResponseFile + ResponseFileTokenReplacer = Helpers.TryReadResponseFile, + EnableDefaultExceptionHandler = false, }.Invoke(args); public static void PrintWarning(string warning)