diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index fc83086fad4719..e21dd9e89bf8a1 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "9.0.0-prerelease.25228.2", + "version": "9.0.0-prerelease.25269.3", "commands": [ "xharness" ] diff --git a/NuGet.config b/NuGet.config index 5950278d8673db..e4806514865c0f 100644 --- a/NuGet.config +++ b/NuGet.config @@ -13,7 +13,7 @@ - + - + https://github.com/dotnet/cecil - 6aaf3af113593a4c993854bff4141bbc73061ea5 + bbb895e8e9f2d566eae04f09977b8c5f895057d2 @@ -92,139 +92,139 @@ - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 - + https://github.com/dotnet/runtime-assets - c9371153c0f06168c3344b806331a29389d1171e + 7f6eab719b1c6834f694cfddb73c3891942e31e4 - + https://github.com/dotnet/runtime-assets - c9371153c0f06168c3344b806331a29389d1171e + 7f6eab719b1c6834f694cfddb73c3891942e31e4 - + https://github.com/dotnet/runtime-assets - c9371153c0f06168c3344b806331a29389d1171e + 7f6eab719b1c6834f694cfddb73c3891942e31e4 - + https://github.com/dotnet/runtime-assets - c9371153c0f06168c3344b806331a29389d1171e + 7f6eab719b1c6834f694cfddb73c3891942e31e4 - + https://github.com/dotnet/runtime-assets - c9371153c0f06168c3344b806331a29389d1171e + 7f6eab719b1c6834f694cfddb73c3891942e31e4 - + https://github.com/dotnet/runtime-assets - c9371153c0f06168c3344b806331a29389d1171e + 7f6eab719b1c6834f694cfddb73c3891942e31e4 - + https://github.com/dotnet/runtime-assets - c9371153c0f06168c3344b806331a29389d1171e + 7f6eab719b1c6834f694cfddb73c3891942e31e4 - + https://github.com/dotnet/runtime-assets - c9371153c0f06168c3344b806331a29389d1171e + 7f6eab719b1c6834f694cfddb73c3891942e31e4 - + https://github.com/dotnet/runtime-assets - c9371153c0f06168c3344b806331a29389d1171e + 7f6eab719b1c6834f694cfddb73c3891942e31e4 - + https://github.com/dotnet/runtime-assets - c9371153c0f06168c3344b806331a29389d1171e + 7f6eab719b1c6834f694cfddb73c3891942e31e4 - + https://github.com/dotnet/runtime-assets - c9371153c0f06168c3344b806331a29389d1171e + 7f6eab719b1c6834f694cfddb73c3891942e31e4 - + https://github.com/dotnet/runtime-assets - c9371153c0f06168c3344b806331a29389d1171e + 7f6eab719b1c6834f694cfddb73c3891942e31e4 - + https://github.com/dotnet/runtime-assets - c9371153c0f06168c3344b806331a29389d1171e + 7f6eab719b1c6834f694cfddb73c3891942e31e4 https://github.com/dotnet/llvm-project @@ -320,21 +320,21 @@ https://github.com/dotnet/runtime b030c4dfdfa1bf287f10f96006619a06bc2000ae - + https://github.com/dotnet/xharness - 5e21f691093804fcd40096109c51a96334ac38cc + 4d797652fa667e94a39db06cbb5a3cad4f72a51f - + https://github.com/dotnet/xharness - 5e21f691093804fcd40096109c51a96334ac38cc + 4d797652fa667e94a39db06cbb5a3cad4f72a51f - + https://github.com/dotnet/xharness - 5e21f691093804fcd40096109c51a96334ac38cc + 4d797652fa667e94a39db06cbb5a3cad4f72a51f - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + 0d52a8b262d35fa2fde84e398cb2e791b8454bd2 https://dev.azure.com/dnceng/internal/_git/dotnet-optimization @@ -356,19 +356,19 @@ https://github.com/dotnet/hotreload-utils 46df3d5e763fdd0e57eeafcb898a86bb955483cb - + https://github.com/dotnet/runtime-assets - c9371153c0f06168c3344b806331a29389d1171e + 7f6eab719b1c6834f694cfddb73c3891942e31e4 - + https://github.com/dotnet/roslyn 3f5cf9fbbd91f2047e988801a5142ca1cb6bab45 - + https://github.com/dotnet/roslyn 3f5cf9fbbd91f2047e988801a5142ca1cb6bab45 - + https://github.com/dotnet/roslyn 3f5cf9fbbd91f2047e988801a5142ca1cb6bab45 @@ -381,19 +381,19 @@ 16865ea61910500f1022ad2b96c499e5df02c228 - + https://github.com/dotnet/roslyn 3f5cf9fbbd91f2047e988801a5142ca1cb6bab45 https://github.com/dotnet/sdk - b562a3008a23dc5e7dca299c31056684b16b617e + 38e51fe4e1fa36644ea66191001e82078989f051 - + https://github.com/dotnet/sdk - b562a3008a23dc5e7dca299c31056684b16b617e + 38e51fe4e1fa36644ea66191001e82078989f051 diff --git a/eng/Versions.props b/eng/Versions.props index bdad4e07fca67b..1658031bdfbee1 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.12.0-3.25256.6 - 4.12.0-3.25256.6 - 4.12.0-3.25256.6 + 4.12.0-3.25275.3 + 4.12.0-3.25275.3 + 4.12.0-3.25275.3 9.0.107 - 9.0.0-beta.25255.5 - 9.0.0-beta.25255.5 - 9.0.0-beta.25255.5 - 9.0.0-beta.25255.5 - 2.9.0-beta.25255.5 - 9.0.0-beta.25255.5 - 2.9.0-beta.25255.5 - 9.0.0-beta.25255.5 - 9.0.0-beta.25255.5 - 9.0.0-beta.25255.5 - 9.0.0-beta.25255.5 - 9.0.0-beta.25255.5 - 9.0.0-beta.25255.5 - 9.0.0-beta.25255.5 - 9.0.0-beta.25255.5 - 9.0.0-beta.25255.5 + 9.0.0-beta.25302.2 + 9.0.0-beta.25302.2 + 9.0.0-beta.25302.2 + 9.0.0-beta.25302.2 + 2.9.0-beta.25302.2 + 9.0.0-beta.25302.2 + 2.9.0-beta.25302.2 + 9.0.0-beta.25302.2 + 9.0.0-beta.25302.2 + 9.0.0-beta.25302.2 + 9.0.0-beta.25302.2 + 9.0.0-beta.25302.2 + 9.0.0-beta.25302.2 + 9.0.0-beta.25302.2 + 9.0.0-beta.25302.2 + 9.0.0-beta.25302.2 1.4.0 @@ -141,20 +141,20 @@ 8.0.0 8.0.0 - 9.0.0-beta.25211.3 - 9.0.0-beta.25211.3 - 9.0.0-beta.25211.3 - 9.0.0-beta.25211.3 - 9.0.0-beta.25211.3 - 9.0.0-beta.25211.3 - 9.0.0-beta.25211.3 - 9.0.0-beta.25211.3 - 9.0.0-beta.25211.3 - 9.0.0-beta.25211.3 - 9.0.0-beta.25211.3 - 9.0.0-beta.25211.3 - 9.0.0-beta.25211.3 - 9.0.0-beta.25211.3 + 9.0.0-beta.25266.2 + 9.0.0-beta.25266.2 + 9.0.0-beta.25266.2 + 9.0.0-beta.25266.2 + 9.0.0-beta.25266.2 + 9.0.0-beta.25266.2 + 9.0.0-beta.25266.2 + 9.0.0-beta.25266.2 + 9.0.0-beta.25266.2 + 9.0.0-beta.25266.2 + 9.0.0-beta.25266.2 + 9.0.0-beta.25266.2 + 9.0.0-beta.25266.2 + 9.0.0-beta.25266.2 1.0.0-prerelease.24462.2 1.0.0-prerelease.24462.2 @@ -184,9 +184,9 @@ 1.4.0 17.4.0-preview-20220707-01 - 9.0.0-prerelease.25228.2 - 9.0.0-prerelease.25228.2 - 9.0.0-prerelease.25228.2 + 9.0.0-prerelease.25269.3 + 9.0.0-prerelease.25269.3 + 9.0.0-prerelease.25269.3 9.0.0-alpha.0.25209.2 3.12.0 4.5.0 @@ -215,11 +215,11 @@ 9.0.0-preview-20241010.1 - 0.11.5-alpha.25228.2 + 0.11.5-alpha.25275.2 9.0.0-rtm.24511.16 - 9.0.0-rtm.25258.1 + 9.0.0-rtm.25304.1 9.0.0-rtm.24466.4 2.4.8 @@ -263,7 +263,7 @@ 1.0.406601 - 9.0.105 + 9.0.106 9.0.0-alpha.1.24175.1 $(MicrosoftNETRuntimeEmscriptenVersion) $(runtimewinx64MicrosoftNETCoreRuntimeWasmNodeTransportPackageVersion) diff --git a/eng/common/core-templates/post-build/post-build.yml b/eng/common/core-templates/post-build/post-build.yml index 454fd75c7aff19..a8c0bd3b9214e5 100644 --- a/eng/common/core-templates/post-build/post-build.yml +++ b/eng/common/core-templates/post-build/post-build.yml @@ -44,6 +44,11 @@ parameters: displayName: Publish installers and checksums type: boolean default: true + + - name: requireDefaultChannels + displayName: Fail the build if there are no default channel(s) registrations for the current build + type: boolean + default: false - name: SDLValidationParameters type: object @@ -312,5 +317,6 @@ stages: -PublishingInfraVersion ${{ parameters.publishingInfraVersion }} -AzdoToken '$(System.AccessToken)' -WaitPublishingFinish true + -RequireDefaultChannels ${{ parameters.requireDefaultChannels }} -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}' -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}' diff --git a/eng/common/post-build/publish-using-darc.ps1 b/eng/common/post-build/publish-using-darc.ps1 index 90b58e32a87bfb..a261517ef90679 100644 --- a/eng/common/post-build/publish-using-darc.ps1 +++ b/eng/common/post-build/publish-using-darc.ps1 @@ -5,7 +5,8 @@ param( [Parameter(Mandatory=$false)][string] $MaestroApiEndPoint = 'https://maestro.dot.net', [Parameter(Mandatory=$true)][string] $WaitPublishingFinish, [Parameter(Mandatory=$false)][string] $ArtifactsPublishingAdditionalParameters, - [Parameter(Mandatory=$false)][string] $SymbolPublishingAdditionalParameters + [Parameter(Mandatory=$false)][string] $SymbolPublishingAdditionalParameters, + [Parameter(Mandatory=$false)][string] $RequireDefaultChannels ) try { @@ -33,6 +34,10 @@ try { if ("false" -eq $WaitPublishingFinish) { $optionalParams.Add("--no-wait") | Out-Null } + + if ("true" -eq $RequireDefaultChannels) { + $optionalParams.Add("--default-channels-required") | Out-Null + } & $darc add-build-to-channel ` --id $buildId ` diff --git a/global.json b/global.json index c2eefc4a16603b..e7a948a8c83a06 100644 --- a/global.json +++ b/global.json @@ -1,16 +1,16 @@ { "sdk": { - "version": "9.0.105", + "version": "9.0.106", "allowPrerelease": true, "rollForward": "major" }, "tools": { - "dotnet": "9.0.105" + "dotnet": "9.0.106" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.25255.5", - "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.25255.5", - "Microsoft.DotNet.SharedFramework.Sdk": "9.0.0-beta.25255.5", + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.25302.2", + "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.25302.2", + "Microsoft.DotNet.SharedFramework.Sdk": "9.0.0-beta.25302.2", "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.4.0", "Microsoft.NET.Sdk.IL": "9.0.0-rtm.24511.16" diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 95cf5c545418fa..c80e14e88f57e6 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -10717,6 +10717,14 @@ size_t gc_heap::sort_mark_list() size_t region_index = get_basic_region_index_for_address (heap_segment_mem (region)); uint8_t* region_limit = heap_segment_allocated (region); + // Due to GC holes, x can point to something in a region that already got freed. And that region's + // allocated would be 0 and cause an infinite loop which is much harder to handle on production than + // simply throwing an exception. + if (region_limit == 0) + { + FATAL_GC_ERROR(); + } + uint8_t*** mark_list_piece_start_ptr = &mark_list_piece_start[region_index]; uint8_t*** mark_list_piece_end_ptr = &mark_list_piece_end[region_index]; #else // USE_REGIONS diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 4102d4c7711150..32506047d2269d 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -6268,6 +6268,18 @@ void CodeGen::genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackA { retSize = emitTypeSize(retTypeDesc->GetReturnRegType(0)); secondRetSize = emitTypeSize(retTypeDesc->GetReturnRegType(1)); + + if (retTypeDesc->GetABIReturnReg(1, call->GetUnmanagedCallConv()) == REG_INTRET) + { + // If the second return register is REG_INTRET, then the first + // return is expected to be in a SIMD register. + // The emitter has hardcoded belief that retSize corresponds to + // REG_INTRET and secondRetSize to REG_INTRET_1, so fix up the + // situation here. + assert(!EA_IS_GCREF_OR_BYREF(retSize)); + retSize = secondRetSize; + secondRetSize = EA_UNKNOWN; + } } else { diff --git a/src/coreclr/jit/gcencode.cpp b/src/coreclr/jit/gcencode.cpp index ae05bf797f86ab..a1e9f935327d37 100644 --- a/src/coreclr/jit/gcencode.cpp +++ b/src/coreclr/jit/gcencode.cpp @@ -49,8 +49,21 @@ ReturnKind GCInfo::getReturnKind() case 1: return VarTypeToReturnKind(retTypeDesc.GetReturnRegType(0)); case 2: - return GetStructReturnKind(VarTypeToReturnKind(retTypeDesc.GetReturnRegType(0)), - VarTypeToReturnKind(retTypeDesc.GetReturnRegType(1))); + { + var_types first = retTypeDesc.GetReturnRegType(0); + var_types second = retTypeDesc.GetReturnRegType(1); +#ifdef UNIX_AMD64_ABI + if (varTypeUsesFloatReg(first)) + { + // first does not consume an int register in this case so an obj/ref + // in the second ReturnKind would actually be found in the first int reg. + first = second; + second = TYP_UNDEF; + } +#endif // UNIX_AMD64_ABI + return GetStructReturnKind(VarTypeToReturnKind(first), + VarTypeToReturnKind(second)); + } default: #ifdef DEBUG for (unsigned i = 0; i < regCount; i++) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 20645cbc3660f8..76660c67f26f42 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -30167,7 +30167,7 @@ unsigned* SsaNumInfo::GetOutlinedNumSlot(Compiler* compiler, unsigned index) con // Copy over all of the already encoded numbers. if (!baseNum.IsInvalid()) { - for (int i = 0; i < SIMPLE_NUM_COUNT; i++) + for (int i = 0; i < count; i++) { pFirstSlot[i] = baseNum.GetNum(compiler, i); } diff --git a/src/coreclr/jit/morphblock.cpp b/src/coreclr/jit/morphblock.cpp index f21cc2355a4861..ed1479804ceb6b 100644 --- a/src/coreclr/jit/morphblock.cpp +++ b/src/coreclr/jit/morphblock.cpp @@ -250,8 +250,6 @@ void MorphInitBlockHelper::PropagateBlockAssertions() // void MorphInitBlockHelper::PropagateExpansionAssertions() { - // Consider doing this for FieldByField as well - // if (m_comp->optLocalAssertionProp && (m_transformationDecision == BlockTransformation::OneStoreBlock)) { m_comp->fgAssertionGen(m_store); @@ -408,6 +406,7 @@ void MorphInitBlockHelper::TryInitFieldByField() if (m_comp->fgGlobalMorph && m_dstLclNode->IsLastUse(i)) { JITDUMP("Field-by-field init skipping write to dead field V%02u\n", fieldLclNum); + m_comp->fgKillDependentAssertionsSingle(m_dstLclNum DEBUGARG(m_store)); continue; } @@ -1236,6 +1235,7 @@ GenTree* MorphCopyBlockHelper::CopyFieldByField() { INDEBUG(unsigned dstFieldLclNum = m_comp->lvaGetDesc(m_dstLclNum)->lvFieldLclStart + i); JITDUMP("Field-by-field copy skipping write to dead field V%02u\n", dstFieldLclNum); + m_comp->fgKillDependentAssertionsSingle(m_dstLclNum DEBUGARG(m_store)); continue; } diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp index 8b7239896a0ea6..50607f57d9e911 100644 --- a/src/coreclr/vm/method.cpp +++ b/src/coreclr/vm/method.cpp @@ -1324,6 +1324,15 @@ ReturnKind MethodDesc::ParseReturnKindFromSig(INDEBUG(bool supportStringConstruc regKinds[i] = RT_Scalar; } } + + if (eeClass->GetEightByteClassification(0) == SystemVClassificationTypeSSE) + { + // Skip over SSE types since they do not consume integer registers. + // An obj/byref in the 2nd eight bytes will be in the first integer register. + regKinds[0] = regKinds[1]; + regKinds[1] = RT_Scalar; + } + ReturnKind structReturnKind = GetStructReturnKind(regKinds[0], regKinds[1]); return structReturnKind; } @@ -3687,10 +3696,14 @@ MethodDesc::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) ILCodeVersion ilVersion = pCodeVersionManager->GetActiveILCodeVersion(dac_cast(this)); if (!ilVersion.IsNull()) { - ilVersion.GetActiveNativeCodeVersion(dac_cast(this)); - ilVersion.GetVersionId(); - ilVersion.GetRejitState(); - ilVersion.GetIL(); + EX_TRY + { + ilVersion.GetActiveNativeCodeVersion(dac_cast(this)); + ilVersion.GetVersionId(); + ilVersion.GetRejitState(); + ilVersion.GetIL(); + } + EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED } #endif diff --git a/src/installer/tests/HostActivation.Tests/SymbolicLinks.cs b/src/installer/tests/HostActivation.Tests/SymbolicLinks.cs index d440d3eae2caa2..5afab32a0881d2 100644 --- a/src/installer/tests/HostActivation.Tests/SymbolicLinks.cs +++ b/src/installer/tests/HostActivation.Tests/SymbolicLinks.cs @@ -2,8 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using FluentAssertions; using Microsoft.DotNet.Cli.Build.Framework; @@ -21,6 +23,165 @@ public SymbolicLinks(SymbolicLinks.SharedTestState fixture) sharedTestState = fixture; } + [Theory] + [InlineData("a/b/SymlinkToFrameworkDependentApp")] + [InlineData("a/SymlinkToFrameworkDependentApp")] + public void Symlink_all_files_fx(string symlinkRelativePath) + { + using var testDir = TestArtifact.Create("symlink"); + Directory.CreateDirectory(Path.Combine(testDir.Location, Path.GetDirectoryName(symlinkRelativePath))); + + // Symlink every file in the app directory + var symlinks = new List(); + try + { + foreach (var file in Directory.EnumerateFiles(sharedTestState.FrameworkDependentApp.Location)) + { + var fileName = Path.GetFileName(file); + var symlinkPath = Path.Combine(testDir.Location, symlinkRelativePath, fileName); + Directory.CreateDirectory(Path.GetDirectoryName(symlinkPath)); + symlinks.Add(new SymLink(symlinkPath, file)); + } + + var result = Command.Create(Path.Combine(testDir.Location, symlinkRelativePath, Path.GetFileName(sharedTestState.FrameworkDependentApp.AppExe))) + .CaptureStdErr() + .CaptureStdOut() + .DotNetRoot(TestContext.BuiltDotNet.BinPath) + .Execute(); + + // This should succeed on all platforms, but for different reasons: + // * Windows: The apphost will look next to the symlink for the app dll and find the symlinked dll + // * Unix: The apphost will look next to the resolved apphost for the app dll and find the real thing + result + .Should().Pass() + .And.HaveStdOutContaining("Hello World"); + } + finally + { + foreach (var symlink in symlinks) + { + symlink.Dispose(); + } + } + } + + [Theory] + [InlineData("a/b/SymlinkToFrameworkDependentApp")] + [InlineData("a/SymlinkToFrameworkDependentApp")] + public void Symlink_split_files_fx(string symlinkRelativePath) + { + using var testDir = TestArtifact.Create("symlink"); + + // Split the app into two directories, one for the apphost and one for the rest of the files + var appHostDir = Path.Combine(testDir.Location, "apphost"); + var appFilesDir = Path.Combine(testDir.Location, "appfiles"); + Directory.CreateDirectory(appHostDir); + Directory.CreateDirectory(appFilesDir); + + var appHostName = Path.GetFileName(sharedTestState.FrameworkDependentApp.AppExe); + + File.Copy( + sharedTestState.FrameworkDependentApp.AppExe, + Path.Combine(appHostDir, appHostName)); + + foreach (var file in Directory.EnumerateFiles(sharedTestState.FrameworkDependentApp.Location)) + { + var fileName = Path.GetFileName(file); + if (fileName != appHostName) + { + File.Copy(file, Path.Combine(appFilesDir, fileName)); + } + } + + // Symlink all of the above into a single directory + var targetPath = Path.Combine(testDir.Location, symlinkRelativePath); + Directory.CreateDirectory(targetPath); + var symlinks = new List(); + try + { + foreach (var file in Directory.EnumerateFiles(appFilesDir)) + { + var fileName = Path.GetFileName(file); + var symlinkPath = Path.Combine(targetPath, fileName); + Directory.CreateDirectory(Path.GetDirectoryName(symlinkPath)); + symlinks.Add(new SymLink(symlinkPath, file)); + } + symlinks.Add(new SymLink( + Path.Combine(targetPath, appHostName), + Path.Combine(appHostDir, appHostName))); + + var result = Command.Create(Path.Combine(targetPath, appHostName)) + .CaptureStdErr() + .CaptureStdOut() + .DotNetRoot(TestContext.BuiltDotNet.BinPath) + .Execute(); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // On Windows, the apphost will look next to the symlink for the app dll and find the symlinks + result + .Should().Pass() + .And.HaveStdOutContaining("Hello World"); + } + else + { + // On Unix, the apphost will not find the app files next to the symlink + result + .Should().Fail() + .And.HaveStdErrContaining("The application to execute does not exist"); + } + } + finally + { + foreach (var symlink in symlinks) + { + symlink.Dispose(); + } + } + } + + [Theory] + [InlineData("a/b/SymlinkToFrameworkDependentApp")] + [InlineData("a/SymlinkToFrameworkDependentApp")] + public void Symlink_all_files_self_contained(string symlinkRelativePath) + { + using var testDir = TestArtifact.Create("symlink"); + Directory.CreateDirectory(Path.Combine(testDir.Location, Path.GetDirectoryName(symlinkRelativePath))); + + // Symlink every file in the app directory + var symlinks = new List(); + try + { + foreach (var file in Directory.EnumerateFiles(sharedTestState.SelfContainedApp.Location)) + { + var fileName = Path.GetFileName(file); + var symlinkPath = Path.Combine(testDir.Location, symlinkRelativePath, fileName); + Directory.CreateDirectory(Path.GetDirectoryName(symlinkPath)); + symlinks.Add(new SymLink(symlinkPath, file)); + } + + var result = Command.Create(Path.Combine(testDir.Location, symlinkRelativePath, Path.GetFileName(sharedTestState.FrameworkDependentApp.AppExe))) + .CaptureStdErr() + .CaptureStdOut() + .DotNetRoot(TestContext.BuiltDotNet.BinPath) + .Execute(); + + // This should succeed on all platforms, but for different reasons: + // * Windows: The apphost will look next to the symlink for the files and find the symlinks + // * Unix: The apphost will look next to the resolved apphost for the files and find the real thing + result + .Should().Pass() + .And.HaveStdOutContaining("Hello World"); + } + finally + { + foreach (var symlink in symlinks) + { + symlink.Dispose(); + } + } + } + [Theory] [InlineData ("a/b/SymlinkToApphost")] [InlineData ("a/SymlinkToApphost")] @@ -33,12 +194,23 @@ public void Run_apphost_behind_symlink(string symlinkRelativePath) var symlinkFullPath = Path.Combine(testDir.Location, symlinkRelativePath); using var symlink = new SymLink(symlinkFullPath, sharedTestState.SelfContainedApp.AppExe); - Command.Create(symlinkFullPath) + var result = Command.Create(symlinkFullPath) .CaptureStdErr() .CaptureStdOut() - .Execute() - .Should().Pass() - .And.HaveStdOutContaining("Hello World"); + .Execute(); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + result + .Should().Fail() + .And.HaveStdErrContaining("The application to execute does not exist"); + } + else + { + result + .Should().Pass() + .And.HaveStdOutContaining("Hello World"); + } } } @@ -63,12 +235,23 @@ public void Run_apphost_behind_transitive_symlinks(string firstSymlinkRelativePa Directory.CreateDirectory(Path.GetDirectoryName(symlink1Path)); using var symlink1 = new SymLink(symlink1Path, symlink2Path); - Command.Create(symlink1.SrcPath) + var result = Command.Create(symlink1.SrcPath) .CaptureStdErr() .CaptureStdOut() - .Execute() - .Should().Pass() - .And.HaveStdOutContaining("Hello World"); + .Execute(); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + result + .Should().Fail() + .And.HaveStdErrContaining("The application to execute does not exist"); + } + else + { + result + .Should().Pass() + .And.HaveStdOutContaining("Hello World"); + } } } @@ -86,13 +269,24 @@ public void Run_framework_dependent_app_behind_symlink(string symlinkRelativePat Directory.CreateDirectory(Path.Combine(testDir.Location, Path.GetDirectoryName(symlinkRelativePath))); using var symlink = new SymLink(Path.Combine(testDir.Location, symlinkRelativePath), sharedTestState.FrameworkDependentApp.AppExe); - Command.Create(symlink.SrcPath) + var result = Command.Create(symlink.SrcPath) .CaptureStdErr() .CaptureStdOut() .DotNetRoot(TestContext.BuiltDotNet.BinPath) - .Execute() - .Should().Pass() - .And.HaveStdOutContaining("Hello World"); + .Execute(); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + result + .Should().Fail() + .And.HaveStdErrContaining("The application to execute does not exist"); + } + else + { + result + .Should().Pass() + .And.HaveStdOutContaining("Hello World"); + } } } @@ -144,12 +338,23 @@ public void Put_dotnet_behind_symlink() var dotnetSymlink = Path.Combine(testDir.Location, Binaries.DotNet.FileName); using var symlink = new SymLink(dotnetSymlink, TestContext.BuiltDotNet.DotnetExecutablePath); - Command.Create(symlink.SrcPath, sharedTestState.SelfContainedApp.AppDll) + var result = Command.Create(symlink.SrcPath, sharedTestState.SelfContainedApp.AppDll) .CaptureStdErr() .CaptureStdOut() - .Execute() - .Should().Pass() - .And.HaveStdOutContaining("Hello World"); + .Execute(); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + result + .Should().Fail() + .And.HaveStdErrContaining($"[{Path.Combine(testDir.Location, "host", "fxr")}] does not exist"); + } + else + { + result + .Should().Pass() + .And.HaveStdOutContaining("Hello World"); + } } } diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs index 9f3d05e43e96af..b9d755d988203f 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs @@ -123,7 +123,14 @@ internal static unsafe ReadOnlySpan SslGetAlpnSelected(SafeSslHandle ssl) internal static partial IntPtr SslGetCertificate(IntPtr ssl); [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslGetPeerCertChain")] - internal static partial SafeSharedX509StackHandle SslGetPeerCertChain(SafeSslHandle ssl); + private static partial SafeSharedX509StackHandle SslGetPeerCertChain_private(SafeSslHandle ssl); + + internal static SafeSharedX509StackHandle SslGetPeerCertChain(SafeSslHandle ssl) + { + return SafeInteriorHandle.OpenInteriorHandle( + SslGetPeerCertChain_private, + ssl); + } [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslGetPeerFinished")] internal static partial int SslGetPeerFinished(SafeSslHandle ssl, IntPtr buf, int count); diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs index 4f6db79a54bf5d..7a0b6dc8e37055 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Unix.cs @@ -139,10 +139,7 @@ internal override void DisposeCore(bool disposing) if (disposing) { - if (State != PipeState.Closed) - { - _internalTokenSource.Cancel(); - } + _internalTokenSource.Cancel(); } } diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Windows.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Windows.cs index c0f066da805214..b986a7342538ba 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Windows.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Windows.cs @@ -62,7 +62,11 @@ internal override void TryToReuse(PipeValueTaskSource source) { if (Interlocked.CompareExchange(ref _reusableConnectionValueTaskSource, connectionSource, null) is not null) { - source._preallocatedOverlapped.Dispose(); + source.Dispose(); + } + else if (State == PipeState.Closed) + { + Interlocked.Exchange(ref _reusableConnectionValueTaskSource, null)?.Dispose(); } } } diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Windows.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Windows.cs index 453fe1153bd4be..ddff733119a7a8 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Windows.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Windows.cs @@ -255,9 +255,25 @@ internal virtual void TryToReuse(PipeValueTaskSource source) if (source is ReadWriteValueTaskSource readWriteSource) { ref ReadWriteValueTaskSource? field = ref readWriteSource._isWrite ? ref _reusableWriteValueTaskSource : ref _reusableReadValueTaskSource; + + // Try to return the instance. If there's already something in the field, then there had been concurrent + // operations and someone else already returned their instance, so just dispose of this one (the "pool" has + // a size of 1). If there's not something there and we're successful in storing our instance, the 99% case is + // this is normal operation, there wasn't a concurrent operation, and we just successfully returned the rented + // instance. However, it could also be because the stream has been disposed, in which case the disposal process + // would have nulled out the field, and since this instance wasn't present when disposal happened, it won't have + // been disposed. Further, disposal won't happen again, so just leaving the instance there will result in its + // resources being leaked. Instead, _after_ returning the instance we then check whether the stream is now + // disposed, and if it is, we then try to re-rent the instance and dispose of it. It's fine if the disposal happens + // after we've stored the instance back and before we check the stream's state, as then this code will be racing + // with disposal and only one of the two will succeed in exchanging out the instance. if (Interlocked.CompareExchange(ref field, readWriteSource, null) is not null) { - source._preallocatedOverlapped.Dispose(); + source.Dispose(); + } + else if (State == PipeState.Closed) + { + Interlocked.Exchange(ref field, null)?.Dispose(); } } } diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.cs index a52b3cac99bc1b..d1fdf314344a50 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.cs @@ -144,6 +144,11 @@ public override Task FlushAsync(CancellationToken cancellationToken) protected override void Dispose(bool disposing) { + // Mark the pipe as closed before calling DisposeCore. That way, other threads that might + // be synchronizing on shared resources disposed of in DisposeCore will be guaranteed to + // see the closed state after that synchronization. + _state = PipeState.Closed; + try { // Nothing will be done differently based on whether we are @@ -159,8 +164,6 @@ protected override void Dispose(bool disposing) { base.Dispose(disposing); } - - _state = PipeState.Closed; } // ********************** Public Properties *********************** // diff --git a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Windows.cs b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Windows.cs index 72fc7685dffe90..4585e92a8ac9dc 100644 --- a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Windows.cs +++ b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Windows.cs @@ -437,6 +437,11 @@ public void RegisterForCancellation(CancellationToken cancellationToken) NetEventSource.Info(@this, $"GetAddrInfoExCancel returned error {cancelResult}"); } } + catch (ObjectDisposedException) + { + // There is a race between checking @this._completed and @this.DangerousAddRef and disposing from another thread. + // We lost the race. No further action needed. + } finally { if (needRelease) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs index db0a4a6c84bf19..bb6407ea4e0e43 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs @@ -1057,8 +1057,9 @@ internal bool VerifyRemoteCertificate(RemoteCertificateValidationCallback? remot return true; } - _remoteCertificate = certificate; - if (_remoteCertificate == null) + // don't assign to _remoteCertificate yet, this prevents weird exceptions if SslStream is disposed in parallel with X509Chain building + + if (certificate == null) { if (NetEventSource.Log.IsEnabled() && RemoteCertRequired) NetEventSource.Error(this, $"Remote certificate required, but no remote certificate received"); sslPolicyErrors |= SslPolicyErrors.RemoteCertificateNotAvailable; @@ -1100,15 +1101,17 @@ internal bool VerifyRemoteCertificate(RemoteCertificateValidationCallback? remot sslPolicyErrors |= CertificateValidationPal.VerifyCertificateProperties( _securityContext!, chain, - _remoteCertificate, + certificate, _sslAuthenticationOptions.CheckCertName, _sslAuthenticationOptions.IsServer, TargetHostNameHelper.NormalizeHostName(_sslAuthenticationOptions.TargetHost)); } + _remoteCertificate = certificate; + if (remoteCertValidationCallback != null) { - success = remoteCertValidationCallback(this, _remoteCertificate, chain, sslPolicyErrors); + success = remoteCertValidationCallback(this, certificate, chain, sslPolicyErrors); } else { diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamDisposeTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamDisposeTest.cs index de7aa502933b02..9864029c7a0b34 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamDisposeTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamDisposeTest.cs @@ -4,6 +4,7 @@ using System.IO; using System.Security.Cryptography.X509Certificates; using System.Threading; +using System.Security.Authentication; using System.Threading.Tasks; using Xunit; @@ -59,6 +60,7 @@ public async Task Dispose_PendingReadAsync_ThrowsODE(bool bufferedRead) using CancellationTokenSource cts = new CancellationTokenSource(); cts.CancelAfter(TestConfiguration.PassingTestTimeout); + (SslStream client, SslStream server) = TestHelper.GetConnectedSslStreams(leaveInnerStreamOpen: true); using (client) using (server) @@ -102,5 +104,65 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout( await Assert.ThrowsAnyAsync(() => client.ReadAsync(readBuffer, cts.Token).AsTask()); } } + + [Fact] + [OuterLoop("Computationally expensive")] + public async Task Dispose_ParallelWithHandshake_ThrowsODE() + { + using CancellationTokenSource cts = new CancellationTokenSource(); + cts.CancelAfter(TestConfiguration.PassingTestTimeout); + + await Parallel.ForEachAsync(System.Linq.Enumerable.Range(0, 10000), cts.Token, async (i, token) => + { + (Stream clientStream, Stream serverStream) = TestHelper.GetConnectedStreams(); + + using SslStream client = new SslStream(clientStream); + using SslStream server = new SslStream(serverStream); + using X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate(); + using X509Certificate2 clientCertificate = Configuration.Certificates.GetClientCertificate(); + + SslClientAuthenticationOptions clientOptions = new SslClientAuthenticationOptions() + { + TargetHost = Guid.NewGuid().ToString("N"), + RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true, + }; + + SslServerAuthenticationOptions serverOptions = new SslServerAuthenticationOptions() + { + ServerCertificate = serverCertificate, + }; + + var clientTask = Task.Run(() => client.AuthenticateAsClientAsync(clientOptions, cts.Token)); + var serverTask = Task.Run(() => server.AuthenticateAsServerAsync(serverOptions, cts.Token)); + + // Dispose the instances while the handshake is in progress. + client.Dispose(); + server.Dispose(); + + await ValidateExceptionAsync(clientTask); + await ValidateExceptionAsync(serverTask); + }); + + static async Task ValidateExceptionAsync(Task task) + { + try + { + await task; + } + catch (InvalidOperationException ex) when (ex.StackTrace?.Contains("System.IO.StreamBuffer.WriteAsync") ?? true) + { + // Writing to a disposed ConnectedStream (test only, does not happen with NetworkStream) + return; + } + catch (Exception ex) when (ex + is ObjectDisposedException // disposed locally + or IOException // disposed remotely (received unexpected EOF) + or AuthenticationException) // disposed wrapped in AuthenticationException or error from platform library + { + // expected + return; + } + } + } } } diff --git a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.OverlappedValueTaskSource.Windows.cs b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.OverlappedValueTaskSource.Windows.cs index c2eab851bd60e2..f8a075cc61c76f 100644 --- a/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.OverlappedValueTaskSource.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.OverlappedValueTaskSource.Windows.cs @@ -35,7 +35,11 @@ private void TryToReuse(OverlappedValueTaskSource source) if (Interlocked.CompareExchange(ref _reusableOverlappedValueTaskSource, source, null) is not null) { - source._preallocatedOverlapped.Dispose(); + source.Dispose(); + } + else if (IsClosed) + { + Interlocked.Exchange(ref _reusableOverlappedValueTaskSource, null)?.Dispose(); } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionMarshallingFails.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionMarshallingFails.cs index e238ca51f24044..dd2759297b3a20 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionMarshallingFails.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.Tests/CollectionMarshallingFails.cs @@ -110,7 +110,8 @@ public static partial void NegateBoolsRef2D_ClearMarshalling( public class CollectionMarshallingFails { [Fact] - public void UTFStringConversionFailures() + [SkipOnCI("Allocates enough memory that the OOM killer can kill the process on our Helix machines.")] + public void BigUTFStringConversionFailures() { bool threw = false; try diff --git a/src/libraries/System.Runtime.Intrinsics/tests/System.Runtime.Intrinsics.Tests.csproj b/src/libraries/System.Runtime.Intrinsics/tests/System.Runtime.Intrinsics.Tests.csproj index e956491c0a63b1..5d98f157e174a3 100644 --- a/src/libraries/System.Runtime.Intrinsics/tests/System.Runtime.Intrinsics.Tests.csproj +++ b/src/libraries/System.Runtime.Intrinsics/tests/System.Runtime.Intrinsics.Tests.csproj @@ -13,6 +13,7 @@ + diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Wasm/PackedSimdTests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Wasm/PackedSimdTests.cs new file mode 100644 index 00000000000000..a53ce142c37cac --- /dev/null +++ b/src/libraries/System.Runtime.Intrinsics/tests/Wasm/PackedSimdTests.cs @@ -0,0 +1,447 @@ +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.Wasm; +using System.Tests; +using Xunit; + +namespace System.Runtime.Intrinsics.Wasm.Tests +{ + [PlatformSpecific(TestPlatforms.Browser)] + public sealed class PackedSimdTests + { + [Fact] + public unsafe void BasicArithmeticTest() + { + var v1 = Vector128.Create(1, 2, 3, 4); + var v2 = Vector128.Create(5, 6, 7, 8); + + var addResult = PackedSimd.Add(v1, v2); + var subResult = PackedSimd.Subtract(v1, v2); + var mulResult = PackedSimd.Multiply(v1, v2); + + Assert.Equal(Vector128.Create(6, 8, 10, 12), addResult); + Assert.Equal(Vector128.Create(-4, -4, -4, -4), subResult); + Assert.Equal(Vector128.Create(5, 12, 21, 32), mulResult); + } + + [Fact] + public unsafe void BitwiseOperationsTest() + { + var v1 = Vector128.Create(0b1100, 0b1010, 0b1110, 0b1111); + var v2 = Vector128.Create(0b1010, 0b1100, 0b0011, 0b0101); + + var andResult = PackedSimd.And(v1, v2); + var orResult = PackedSimd.Or(v1, v2); + var xorResult = PackedSimd.Xor(v1, v2); + + Assert.Equal(Vector128.Create(0b1000, 0b1000, 0b0010, 0b0101), andResult); + Assert.Equal(Vector128.Create(0b1110, 0b1110, 0b1111, 0b1111), orResult); + Assert.Equal(Vector128.Create(0b0110, 0b0110, 0b1101, 0b1010), xorResult); + } + + [Fact] + public unsafe void ShiftOperationsTest() + { + var v = Vector128.Create(16, -16, 32, -32); + + var leftShift = PackedSimd.ShiftLeft(v, 2); + var rightShiftArith = PackedSimd.ShiftRightArithmetic(v, 2); + var rightShiftLogical = PackedSimd.ShiftRightLogical(v, 2); + + Assert.Equal(Vector128.Create(64, -64, 128, -128), leftShift); + Assert.Equal(Vector128.Create(4, -4, 8, -8), rightShiftArith); + Assert.Equal(Vector128.Create(4, 1073741820, 8, 1073741816), rightShiftLogical); + } + + [Fact] + public unsafe void ComparisonOperationsTest() + { + var v1 = Vector128.Create(1.0f, 2.0f, 3.0f, 4.0f); + var v2 = Vector128.Create(4.0f, 3.0f, 2.0f, 1.0f); + + var minResult = PackedSimd.Min(v1, v2); + var maxResult = PackedSimd.Max(v1, v2); + + Assert.Equal(Vector128.Create(1.0f, 2.0f, 2.0f, 1.0f), minResult); + Assert.Equal(Vector128.Create(4.0f, 3.0f, 3.0f, 4.0f), maxResult); + } + + [Fact] + public unsafe void FloatingPointOperationsTest() + { + var v = Vector128.Create(4.0f, 9.0f, 16.0f, 25.0f); + + var sqrtResult = PackedSimd.Sqrt(v); + var ceilResult = PackedSimd.Ceiling(v); + var floorResult = PackedSimd.Floor(v); + + Assert.Equal(Vector128.Create(2.0f, 3.0f, 4.0f, 5.0f), sqrtResult); + Assert.Equal(Vector128.Create(4.0f, 9.0f, 16.0f, 25.0f), ceilResult); + Assert.Equal(Vector128.Create(4.0f, 9.0f, 16.0f, 25.0f), floorResult); + } + + [Fact] + public unsafe void LoadStoreTest() + { + int[] values = new int[] { 1, 2, 3, 4 }; + fixed (int* ptr = values) + { + var loaded = PackedSimd.LoadVector128(ptr); + Assert.Equal(Vector128.Create(1, 2, 3, 4), loaded); + + int[] storeTarget = new int[4]; + fixed (int* storePtr = storeTarget) + { + PackedSimd.Store(storePtr, loaded); + Assert.Equal(values, storeTarget); + } + } + } + + [Fact] + public unsafe void ExtractInsertScalarTest() + { + var v = Vector128.Create(1, 2, 3, 4); + + int extracted = PackedSimd.ExtractScalar(v, 2); + Assert.Equal(3, extracted); + + var modified = PackedSimd.ReplaceScalar(v, 2, 10); + Assert.Equal(Vector128.Create(1, 2, 10, 4), modified); + } + + [Fact] + public unsafe void SaturatingArithmeticTest() + { + var v1 = Vector128.Create((byte)250, (byte)251, (byte)252, (byte)253, (byte)254, (byte)255, (byte)255, (byte)255, + (byte)250, (byte)251, (byte)252, (byte)253, (byte)254, (byte)255, (byte)255, (byte)255); + var v2 = Vector128.Create((byte)10, (byte)10, (byte)10, (byte)10, (byte)10, (byte)10, (byte)10, (byte)10, + (byte)10, (byte)10, (byte)10, (byte)10, (byte)10, (byte)10, (byte)10, (byte)10); + + var addSat = PackedSimd.AddSaturate(v1, v2); + var subSat = PackedSimd.SubtractSaturate(v1, v2); + + // Verify saturation at 255 for addition + Assert.Equal(Vector128.Create((byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, + (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255), addSat); + + // Verify expected subtraction results + Assert.Equal(Vector128.Create((byte)240, (byte)241, (byte)242, (byte)243, (byte)244, (byte)245, (byte)245, (byte)245, + (byte)240, (byte)241, (byte)242, (byte)243, (byte)244, (byte)245, (byte)245, (byte)245), subSat); + } + + [Fact] + public unsafe void WideningOperationsTest() + { + var v = Vector128.Create((short)1000, (short)2000, (short)3000, (short)4000, + (short)5000, (short)6000, (short)7000, (short)8000); + + var lowerWidened = PackedSimd.SignExtendWideningLower(v); + var upperWidened = PackedSimd.SignExtendWideningUpper(v); + + Assert.Equal(Vector128.Create(1000, 2000, 3000, 4000), lowerWidened); + Assert.Equal(Vector128.Create(5000, 6000, 7000, 8000), upperWidened); + } + + [Fact] + public unsafe void SwizzleTest() + { + var v = Vector128.Create((byte)1, (byte)2, (byte)3, (byte)4, (byte)5, (byte)6, (byte)7, (byte)8, + (byte)9, (byte)10, (byte)11, (byte)12, (byte)13, (byte)14, (byte)15, (byte)16); + var indices = Vector128.Create((byte)3, (byte)2, (byte)1, (byte)0, (byte)7, (byte)6, (byte)5, (byte)4, + (byte)11, (byte)10, (byte)9, (byte)8, (byte)15, (byte)14, (byte)13, (byte)12); + + var swizzled = PackedSimd.Swizzle(v, indices); + + Assert.Equal(Vector128.Create((byte)4, (byte)3, (byte)2, (byte)1, (byte)8, (byte)7, (byte)6, (byte)5, + (byte)12, (byte)11, (byte)10, (byte)9, (byte)16, (byte)15, (byte)14, (byte)13), swizzled); + } + + [Fact] + public unsafe void LoadScalarAndSplatTest() + { + int value = 42; + float fValue = 3.14f; + + int* intPtr = &value; + float* floatPtr = &fValue; + + var intSplat = PackedSimd.LoadScalarAndSplatVector128(intPtr); + var floatSplat = PackedSimd.LoadScalarAndSplatVector128(floatPtr); + + Assert.Equal(Vector128.Create(42, 42, 42, 42), intSplat); + Assert.Equal(Vector128.Create(3.14f, 3.14f, 3.14f, 3.14f), floatSplat); + } + + [Fact] + public unsafe void LoadWideningTest() + { + byte[] bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }; + fixed (byte* ptr = bytes) + { + var widened = PackedSimd.LoadWideningVector128(ptr); + Assert.Equal(Vector128.Create((ushort)1, (ushort)2, (ushort)3, (ushort)4, + (ushort)5, (ushort)6, (ushort)7, (ushort)8), widened); + } + } + + [Fact] + public unsafe void StoreSelectedScalarTest() + { + var v = Vector128.Create(1, 2, 3, 4); + int value = 0; + int* ptr = &value; + + PackedSimd.StoreSelectedScalar(ptr, v, 2); + Assert.Equal(3, value); + } + + [Fact] + public unsafe void LoadScalarAndInsertTest() + { + var v = Vector128.Create(1, 2, 3, 4); + int newValue = 42; + int* ptr = &newValue; + + var result = PackedSimd.LoadScalarAndInsert(ptr, v, 2); + Assert.Equal(Vector128.Create(1, 2, 42, 4), result); + } + + [Fact] + public unsafe void ConversionTest() + { + var intVector = Vector128.Create(1, 2, 3, 4); + var floatVector = Vector128.Create(1.5f, 2.5f, 3.5f, 4.5f); + var doubleVector = Vector128.Create(1.5, 2.5); + + var intToFloat = PackedSimd.ConvertToSingle(intVector); + var floatToDouble = PackedSimd.ConvertToDoubleLower(floatVector); + + Assert.Equal(Vector128.Create(1.0f, 2.0f, 3.0f, 4.0f), intToFloat); + Assert.Equal(Vector128.Create(1.5, 2.5), floatToDouble); + } + + [Fact] + public unsafe void AddPairwiseWideningTest() + { + var bytes = Vector128.Create((byte)1, (byte)2, (byte)3, (byte)4, + (byte)5, (byte)6, (byte)7, (byte)8, + (byte)9, (byte)10, (byte)11, (byte)12, + (byte)13, (byte)14, (byte)15, (byte)16); + + var widened = PackedSimd.AddPairwiseWidening(bytes); + + Assert.Equal(Vector128.Create((ushort)3, (ushort)7, (ushort)11, (ushort)15, + (ushort)19, (ushort)23, (ushort)27, (ushort)31), widened); + } + + [Fact] + public unsafe void MultiplyWideningTest() + { + var shorts = Vector128.Create((short)10, (short)20, (short)30, (short)40, + (short)50, (short)60, (short)70, (short)80); + var multiplier = Vector128.Create((short)2, (short)2, (short)2, (short)2, + (short)2, (short)2, (short)2, (short)2); + + var lowerResult = PackedSimd.MultiplyWideningLower(shorts, multiplier); + var upperResult = PackedSimd.MultiplyWideningUpper(shorts, multiplier); + + Assert.Equal(Vector128.Create(20, 40, 60, 80), lowerResult); + Assert.Equal(Vector128.Create(100, 120, 140, 160), upperResult); + } + + [Fact] + public unsafe void DotProductTest() + { + var v1 = Vector128.Create((short)1, (short)2, (short)3, (short)4, + (short)5, (short)6, (short)7, (short)8); + var v2 = Vector128.Create((short)2, (short)2, (short)2, (short)2, + (short)2, (short)2, (short)2, (short)2); + + var dot = PackedSimd.Dot(v1, v2); + + // Each pair of values is multiplied and added: + // (1*2 + 2*2) = 6 for first int + // (3*2 + 4*2) = 14 for second int + // (5*2 + 6*2) = 22 for third int + // (7*2 + 8*2) = 30 for fourth int + Assert.Equal(Vector128.Create(6, 14, 22, 30), dot); + } + + [Fact] + public unsafe void FloatingPointNegationTest() + { + var v = Vector128.Create(1.0f, -2.0f, 3.0f, -4.0f); + var d = Vector128.Create(1.0, -2.0); + + var negatedFloat = PackedSimd.Negate(v); + var negatedDouble = PackedSimd.Negate(d); + + Assert.Equal(Vector128.Create(-1.0f, 2.0f, -3.0f, 4.0f), negatedFloat); + Assert.Equal(Vector128.Create(-1.0, 2.0), negatedDouble); + } + + [Fact] + public unsafe void FloatingPointAbsTest() + { + var v = Vector128.Create(-1.0f, 2.0f, -3.0f, 4.0f); + var d = Vector128.Create(-1.0, 2.0); + + var absFloat = PackedSimd.Abs(v); + var absDouble = PackedSimd.Abs(d); + + Assert.Equal(Vector128.Create(1.0f, 2.0f, 3.0f, 4.0f), absFloat); + Assert.Equal(Vector128.Create(1.0, 2.0), absDouble); + } + + [Fact] + public unsafe void FloatingPointDivisionTest() + { + var v1 = Vector128.Create(2.0f, 4.0f, 6.0f, 8.0f); + var v2 = Vector128.Create(2.0f, 2.0f, 2.0f, 2.0f); + var d1 = Vector128.Create(2.0, 4.0); + var d2 = Vector128.Create(2.0, 2.0); + + var divFloat = PackedSimd.Divide(v1, v2); + var divDouble = PackedSimd.Divide(d1, d2); + + Assert.Equal(Vector128.Create(1.0f, 2.0f, 3.0f, 4.0f), divFloat); + Assert.Equal(Vector128.Create(1.0, 2.0), divDouble); + } + + [Fact] + public unsafe void IntegerAbsTest() + { + var bytes = Vector128.Create((sbyte)-1, (sbyte)2, (sbyte)-3, (sbyte)4, + (sbyte)-5, (sbyte)6, (sbyte)-7, (sbyte)8, + (sbyte)-9, (sbyte)10, (sbyte)-11, (sbyte)12, + (sbyte)-13, (sbyte)14, (sbyte)-15, (sbyte)16); + var shorts = Vector128.Create((short)-1, (short)2, (short)-3, (short)4, + (short)-5, (short)6, (short)-7, (short)8); + var ints = Vector128.Create(-1, 2, -3, 4); + + var absBytes = PackedSimd.Abs(bytes); + var absShorts = PackedSimd.Abs(shorts); + + Assert.Equal(Vector128.Create((sbyte)1, (sbyte)2, (sbyte)3, (sbyte)4, + (sbyte)5, (sbyte)6, (sbyte)7, (sbyte)8, + (sbyte)9, (sbyte)10, (sbyte)11, (sbyte)12, + (sbyte)13, (sbyte)14, (sbyte)15, (sbyte)16), absBytes); + Assert.Equal(Vector128.Create((short)1, (short)2, (short)3, (short)4, + (short)5, (short)6, (short)7, (short)8), absShorts); + } + + [Fact] + public unsafe void AverageRoundedTest() + { + var bytes1 = Vector128.Create((byte)1, (byte)3, (byte)5, (byte)7, + (byte)9, (byte)11, (byte)13, (byte)15, + (byte)17, (byte)19, (byte)21, (byte)23, + (byte)25, (byte)27, (byte)29, (byte)31); + var bytes2 = Vector128.Create((byte)3, (byte)5, (byte)7, (byte)9, + (byte)11, (byte)13, (byte)15, (byte)17, + (byte)19, (byte)21, (byte)23, (byte)25, + (byte)27, (byte)29, (byte)31, (byte)33); + + var avgBytes = PackedSimd.AverageRounded(bytes1, bytes2); + + // Average is rounded up: (a + b + 1) >> 1 + Assert.Equal(Vector128.Create((byte)2, (byte)4, (byte)6, (byte)8, + (byte)10, (byte)12, (byte)14, (byte)16, + (byte)18, (byte)20, (byte)22, (byte)24, + (byte)26, (byte)28, (byte)30, (byte)32), avgBytes); + } + + [Fact] + public unsafe void MinMaxSignedUnsignedTest() + { + var signedBytes = Vector128.Create((sbyte)-1, (sbyte)2, (sbyte)-3, (sbyte)4, + (sbyte)-5, (sbyte)6, (sbyte)-7, (sbyte)8, + (sbyte)-9, (sbyte)10, (sbyte)-11, (sbyte)12, + (sbyte)-13, (sbyte)14, (sbyte)-15, (sbyte)16); + + var unsignedBytes = Vector128.Create((byte)255, (byte)2, (byte)253, (byte)4, + (byte)251, (byte)6, (byte)249, (byte)8, + (byte)247, (byte)10, (byte)245, (byte)12, + (byte)243, (byte)14, (byte)241, (byte)16); + + var signedMin = PackedSimd.Min(signedBytes, signedBytes.WithElement(0, (sbyte)0)); + var unsignedMin = PackedSimd.Min(unsignedBytes, unsignedBytes.WithElement(0, (byte)0)); + + // Verify different comparison behavior for signed vs unsigned + Assert.Equal((sbyte)-1, signedMin.GetElement(0)); + Assert.Equal((byte)0, unsignedMin.GetElement(0)); + } + + [Fact] + public unsafe void LoadScalarAndSplatInfinityTest() + { + float fInf = float.PositiveInfinity; + double dInf = double.PositiveInfinity; + + float* fPtr = &fInf; + double* dPtr = &dInf; + + var floatSplat = PackedSimd.LoadScalarAndSplatVector128(fPtr); + var doubleSplat = PackedSimd.LoadScalarAndSplatVector128(dPtr); + + for (int i = 0; i < 4; i++) + { + Assert.True(float.IsPositiveInfinity(floatSplat.GetElement(i))); + } + + for (int i = 0; i < 2; i++) + { + Assert.True(double.IsPositiveInfinity(doubleSplat.GetElement(i))); + } + } + + [Fact] + public unsafe void FloatingPointTruncateTest() + { + var v1 = Vector128.Create(1.7f, -2.3f, 3.5f, -4.8f); + var d1 = Vector128.Create(1.7, -2.3); + + var truncFloat = PackedSimd.Truncate(v1); + var truncDouble = PackedSimd.Truncate(d1); + + Assert.Equal(Vector128.Create(1.0f, -2.0f, 3.0f, -4.0f), truncFloat); + Assert.Equal(Vector128.Create(1.0, -2.0), truncDouble); + } + + [Fact] + public unsafe void ComparisonWithNaNTest() + { + var v1 = Vector128.Create(1.0f, float.NaN, 3.0f, float.PositiveInfinity); + var v2 = Vector128.Create(float.NegativeInfinity, 2.0f, float.NaN, 4.0f); + + var minResult = PackedSimd.Min(v1, v2); + var maxResult = PackedSimd.Max(v1, v2); + + // IEEE 754 rules: if either operand is NaN, the result should be NaN + Assert.True(float.IsNaN(minResult.GetElement(1))); + Assert.True(float.IsNaN(maxResult.GetElement(2))); + Assert.Equal(float.NegativeInfinity, minResult.GetElement(0)); + Assert.Equal(float.PositiveInfinity, maxResult.GetElement(3)); + } + + [Fact] + public unsafe void NativeIntegerArithmeticTest() + { + var v1 = PackedSimd.Splat((nint)1); + var v2 = PackedSimd.Splat((nint)2); + + var addResult = PackedSimd.Add(v1, v2); + var subResult = PackedSimd.Subtract(v1, v2); + var mulResult = PackedSimd.Multiply(v1, v2); + + Assert.Equal(PackedSimd.Splat((nint)3), addResult); + Assert.Equal(PackedSimd.Splat((nint)(-1)), subResult); + Assert.Equal(PackedSimd.Splat((nint)2), mulResult); + } + } +} \ No newline at end of file diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/FSharpCoreReflectionProxy.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/FSharpCoreReflectionProxy.cs index e0bd83a0c1c9bf..835da6e002f905 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/FSharpCoreReflectionProxy.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/FSharpCoreReflectionProxy.cs @@ -204,7 +204,10 @@ public Func>, TFSharpMap> CreateFSharpMapConstru } private Attribute? GetFSharpCompilationMappingAttribute(Type type) - => type.GetCustomAttribute(_compilationMappingAttributeType, inherit: true); + { + object[] attributes = type.GetCustomAttributes(_compilationMappingAttributeType, inherit: false); + return attributes.Length == 0 ? null : (Attribute)attributes[0]; + } private SourceConstructFlags GetSourceConstructFlags(Attribute compilationMappingAttribute) => _sourceConstructFlagsGetter is null ? SourceConstructFlags.None : (SourceConstructFlags)_sourceConstructFlagsGetter.Invoke(compilationMappingAttribute, null)!; @@ -212,7 +215,7 @@ private SourceConstructFlags GetSourceConstructFlags(Attribute compilationMappin // If the provided type is generated by the F# compiler, returns the runtime FSharp.Core assembly. private static Assembly? GetFSharpCoreAssembly(Type type) { - foreach (Attribute attr in type.GetCustomAttributes(inherit: true)) + foreach (Attribute attr in type.GetCustomAttributes(inherit: false)) { Type attributeType = attr.GetType(); if (attributeType.FullName == CompilationMappingAttributeTypeName) diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/RecordTests.fs b/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/RecordTests.fs index d9ac41cbd2b41d..6044b692904ef4 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/RecordTests.fs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.FSharp.Tests/RecordTests.fs @@ -76,3 +76,22 @@ let ``Recursive struct records are supported``() = Assert.Equal("""{"Next":[{"Next":[null]}]}""", json) let deserializedValue = JsonSerializer.Deserialize(json) Assert.Equal(value, deserializedValue) + + +[, "derived")>] +type BaseClass(x : int) = + member _.X = x + +and DerivedClass(x : int, y : bool) = + inherit BaseClass(x) + member _.Y = y + +[] +let ``Support F# class hierarchies`` () = + let value = DerivedClass(42, true) :> BaseClass + let json = JsonSerializer.Serialize(value) + Assert.Equal("""{"$type":"derived","Y":true,"X":42}""", json) + let deserializedValue = JsonSerializer.Deserialize(json) + let derived = Assert.IsType(deserializedValue) + Assert.Equal(42, deserializedValue.X) + Assert.Equal(true, derived.Y) diff --git a/src/mono/mono/metadata/sgen-bridge.c b/src/mono/mono/metadata/sgen-bridge.c index 579fc0d376cd6a..1f7dc31c9b4b11 100644 --- a/src/mono/mono/metadata/sgen-bridge.c +++ b/src/mono/mono/metadata/sgen-bridge.c @@ -316,24 +316,24 @@ dump_processor_state (SgenBridgeProcessor *p) { int i; - printf ("------\n"); - printf ("SCCS %d\n", p->num_sccs); + g_message ("------\n"); + g_message ("SCCS %d\n", p->num_sccs); for (i = 0; i < p->num_sccs; ++i) { int j; MonoGCBridgeSCC *scc = p->api_sccs [i]; - printf ("\tSCC %d:", i); + g_message ("\tSCC %d:", i); for (j = 0; j < scc->num_objs; ++j) { MonoObject *obj = scc->objs [j]; - printf (" %p(%s)", obj, SGEN_LOAD_VTABLE (obj)->klass->name); + g_message (" %p(%s)", obj, m_class_get_name (SGEN_LOAD_VTABLE (obj)->klass)); } - printf ("\n"); + g_message ("\n"); } - printf ("XREFS %d\n", p->num_xrefs); + g_message ("XREFS %d\n", p->num_xrefs); for (i = 0; i < p->num_xrefs; ++i) - printf ("\t%d -> %d\n", p->api_xrefs [i].src_scc_index, p->api_xrefs [i].dst_scc_index); + g_message ("\t%d -> %d\n", p->api_xrefs [i].src_scc_index, p->api_xrefs [i].dst_scc_index); - printf ("-------\n"); + g_message ("-------\n"); } */ @@ -352,7 +352,7 @@ sgen_compare_bridge_processor_results (SgenBridgeProcessor *a, SgenBridgeProcess if (a->num_sccs != b->num_sccs) g_error ("SCCS count expected %d but got %d", a->num_sccs, b->num_sccs); if (a->num_xrefs != b->num_xrefs) - g_error ("SCCS count expected %d but got %d", a->num_xrefs, b->num_xrefs); + g_error ("XREFS count expected %d but got %d", a->num_xrefs, b->num_xrefs); /* * First we build a hash of each object in `a` to its respective SCC index within diff --git a/src/mono/mono/metadata/sgen-tarjan-bridge.c b/src/mono/mono/metadata/sgen-tarjan-bridge.c index b0c9cf1f83baef..7a8ad5961d98e7 100644 --- a/src/mono/mono/metadata/sgen-tarjan-bridge.c +++ b/src/mono/mono/metadata/sgen-tarjan-bridge.c @@ -400,16 +400,7 @@ static const char* safe_name_bridge (GCObject *obj) { GCVTable vt = SGEN_LOAD_VTABLE (obj); - return vt->klass->name; -} - -static ScanData* -find_or_create_data (GCObject *obj) -{ - ScanData *entry = find_data (obj); - if (!entry) - entry = create_data (obj); - return entry; + return m_class_get_name (vt->klass); } #endif @@ -566,10 +557,15 @@ find_in_cache (int *insert_index) // Populate other_colors for a give color (other_colors represent the xrefs for this color) static void -add_other_colors (ColorData *color, DynPtrArray *other_colors) +add_other_colors (ColorData *color, DynPtrArray *other_colors, gboolean check_visited) { for (int i = 0; i < dyn_array_ptr_size (other_colors); ++i) { ColorData *points_to = (ColorData *)dyn_array_ptr_get (other_colors, i); + if (check_visited) { + if (points_to->visited) + continue; + points_to->visited = TRUE; + } dyn_array_ptr_add (&color->other_colors, points_to); // Inform targets points_to->incoming_colors = MIN (points_to->incoming_colors + 1, INCOMING_COLORS_MAX); @@ -593,7 +589,7 @@ new_color (gboolean has_bridges) cd = alloc_color_data (); cd->api_index = -1; - add_other_colors (cd, &color_merge_array); + add_other_colors (cd, &color_merge_array, FALSE); /* if cacheSlot >= 0, it means we prepared a given slot to receive the new color */ if (cacheSlot >= 0) @@ -700,11 +696,11 @@ compute_low_index (ScanData *data, GCObject *obj) obj = bridge_object_forward (obj); other = find_data (obj); -#if DUMP_GRAPH - printf ("\tcompute low %p ->%p (%s) %p (%d / %d, color %p)\n", data->obj, obj, safe_name_bridge (obj), other, other ? other->index : -2, other ? other->low_index : -2, other->color); -#endif if (!other) return; +#if DUMP_GRAPH + printf ("\tcompute low %p ->%p (%s) %p (%d / %d, color %p)\n", data->obj, obj, safe_name_bridge (obj), other, other ? other->index : -2, other->low_index, other->color); +#endif g_assert (other->state != INITIAL); @@ -777,10 +773,16 @@ create_scc (ScanData *data) gboolean found = FALSE; gboolean found_bridge = FALSE; ColorData *color_data = NULL; + gboolean can_reduce_color = TRUE; for (i = dyn_array_ptr_size (&loop_stack) - 1; i >= 0; --i) { ScanData *other = (ScanData *)dyn_array_ptr_get (&loop_stack, i); found_bridge |= other->is_bridge; + if (dyn_array_ptr_size (&other->xrefs) > 0 || found_bridge) { + // This scc will have more xrefs than the ones from the color_merge_array, + // we will need to create a new color to store this information. + can_reduce_color = FALSE; + } if (found_bridge || other == data) break; } @@ -788,13 +790,15 @@ create_scc (ScanData *data) if (found_bridge) { color_data = new_color (TRUE); ++num_colors_with_bridges; - } else { + } else if (can_reduce_color) { color_data = reduce_color (); + } else { + color_data = new_color (FALSE); } #if DUMP_GRAPH printf ("|SCC %p rooted in %s (%p) has bridge %d\n", color_data, safe_name_bridge (data->obj), data->obj, found_bridge); printf ("\tloop stack: "); - for (int i = 0; i < dyn_array_ptr_size (&loop_stack); ++i) { + for (i = 0; i < dyn_array_ptr_size (&loop_stack); ++i) { ScanData *other = dyn_array_ptr_get (&loop_stack, i); printf ("(%d/%d)", other->index, other->low_index); } @@ -824,10 +828,19 @@ create_scc (ScanData *data) dyn_array_ptr_add (&color_data->bridges, other->obj); } - // Maybe we should make sure we are not adding duplicates here. It is not really a problem - // since we will get rid of duplicates before submitting the SCCs to the client in gather_xrefs - if (color_data) - add_other_colors (color_data, &other->xrefs); + if (dyn_array_ptr_size (&other->xrefs) > 0) { + g_assert (color_data != NULL); + g_assert (can_reduce_color == FALSE); + // We need to eliminate duplicates early otherwise the heaviness property + // can change in gather_xrefs and it breaks down the loop that reports the + // xrefs to the client. + // + // We reuse the visited flag to mark the objects that are already part of + // the color_data array. The array was created above with the new_color call + // and xrefs were populated from color_merge_array, which is already + // deduplicated and every entry is marked as visited. + add_other_colors (color_data, &other->xrefs, TRUE); + } dyn_array_ptr_uninit (&other->xrefs); if (other == data) { @@ -837,11 +850,22 @@ create_scc (ScanData *data) } g_assert (found); + // Clear the visited flag on nodes that were added with add_other_colors in the loop above + if (!can_reduce_color) { + for (i = dyn_array_ptr_size (&color_merge_array); i < dyn_array_ptr_size (&color_data->other_colors); i++) { + ColorData *cd = (ColorData *)dyn_array_ptr_get (&color_data->other_colors, i); + g_assert (cd->visited); + cd->visited = FALSE; + } + } + #if DUMP_GRAPH - printf ("\tpoints-to-colors: "); - for (int i = 0; i < dyn_array_ptr_size (&color_data->other_colors); i++) - printf ("%p ", dyn_array_ptr_get (&color_data->other_colors, i)); - printf ("\n"); + if (color_data) { + printf ("\tpoints-to-colors: "); + for (i = 0; i < dyn_array_ptr_size (&color_data->other_colors); i++) + printf ("%p ", dyn_array_ptr_get (&color_data->other_colors, i)); + printf ("\n"); + } #endif } @@ -966,8 +990,11 @@ dump_color_table (const char *why, gboolean do_index) printf (" bridges: "); for (j = 0; j < dyn_array_ptr_size (&cd->bridges); ++j) { GCObject *obj = dyn_array_ptr_get (&cd->bridges, j); - ScanData *data = find_or_create_data (obj); - printf ("%d ", data->index); + ScanData *data = find_data (obj); + if (!data) + printf ("%p ", obj); + else + printf ("%p(%d) ", obj, data->index); } } printf ("\n"); @@ -1027,7 +1054,7 @@ processing_stw_step (void) #if defined (DUMP_GRAPH) printf ("----summary----\n"); printf ("bridges:\n"); - for (int i = 0; i < bridge_count; ++i) { + for (i = 0; i < bridge_count; ++i) { ScanData *sd = find_data (dyn_array_ptr_get (®istered_bridges, i)); printf ("\t%s (%p) index %d color %p\n", safe_name_bridge (sd->obj), sd->obj, sd->index, sd->color); } @@ -1141,6 +1168,7 @@ processing_build_callback_data (int generation) gather_xrefs (cd); reset_xrefs (cd); dyn_array_ptr_set_all (&cd->other_colors, &color_merge_array); + g_assert (color_visible_to_client (cd)); xref_count += dyn_array_ptr_size (&cd->other_colors); } } diff --git a/src/mono/mono/mini/interp/interp-simd-intrins.def b/src/mono/mono/mini/interp/interp-simd-intrins.def index d88e543af23471..197b3c269d6612 100644 --- a/src/mono/mono/mini/interp/interp-simd-intrins.def +++ b/src/mono/mono/mini/interp/interp-simd-intrins.def @@ -351,7 +351,7 @@ INTERP_WASM_SIMD_INTRINSIC_V_V (ConvertToSingle, U4, wasm_f32x4_convert_u32x4, INTERP_WASM_SIMD_INTRINSIC_V_V (ConvertToSingle, R8, wasm_f32x4_demote_f64x2_zero, 0x5e) INTERP_WASM_SIMD_INTRINSIC_V_V (ConvertToDoubleLower, I4, wasm_f64x2_convert_low_i32x4, 0xfe) INTERP_WASM_SIMD_INTRINSIC_V_V (ConvertToDoubleLower, U4, wasm_f64x2_convert_low_u32x4, 0xff) -INTERP_WASM_SIMD_INTRINSIC_V_V (ConvertToDoubleLower, R8, wasm_f64x2_promote_low_f32x4, 0x5f) +INTERP_WASM_SIMD_INTRINSIC_V_V (ConvertToDoubleLower, R4, wasm_f64x2_promote_low_f32x4, 0x5f) INTERP_WASM_SIMD_INTRINSIC_V_V (ConvertToInt32Saturate, R4, wasm_i32x4_trunc_sat_f32x4, 0xf8) INTERP_WASM_SIMD_INTRINSIC_V_V (ConvertToUInt32Saturate, R4, wasm_u32x4_trunc_sat_f32x4, 0xf9) INTERP_WASM_SIMD_INTRINSIC_V_V (ConvertToInt32Saturate, R8, wasm_i32x4_trunc_sat_f64x2_zero, 0xfc) diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index c3016252942f4a..011d99a9625dda 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -8981,6 +8981,10 @@ mono_ee_interp_init (const char *opts) set_context (NULL); interp_parse_options (opts); + + const char *env_opts = g_getenv ("MONO_INTERPRETER_OPTIONS"); + if (env_opts) + interp_parse_options (env_opts); /* Don't do any optimizations if running under debugger */ if (mini_get_debug_options ()->mdb_optimizations) mono_interp_opt = 0; diff --git a/src/mono/mono/mini/interp/transform-opt.c b/src/mono/mono/mini/interp/transform-opt.c index b259e116c14404..f5ffbc89df7fdd 100644 --- a/src/mono/mono/mini/interp/transform-opt.c +++ b/src/mono/mono/mini/interp/transform-opt.c @@ -3124,6 +3124,7 @@ interp_cprop (TransformData *td) ins->data [2] = GINT_TO_UINT16 (ldsize); interp_clear_ins (ins->prev); + td->var_values [ins->dreg].def = ins; } if (td->verbose_level) { g_print ("Replace ldloca/ldobj_vt pair :\n\t"); @@ -3204,6 +3205,7 @@ interp_cprop (TransformData *td) ins->data [2] = vtsize; interp_clear_ins (ins->prev); + td->var_values [ins->dreg].def = ins; // MINT_MOV_DST_OFF doesn't work if dreg is allocated at the same location as the // field value to be stored, because its behavior is not atomic in nature. We first @@ -3400,9 +3402,11 @@ interp_super_instructions (TransformData *td) current_liveness.bb_dfs_index = bb->dfs_index; current_liveness.ins_index = 0; for (InterpInst *ins = bb->first_ins; ins != NULL; ins = ins->next) { - int opcode = ins->opcode; + int opcode; if (bb->dfs_index >= td->bblocks_count_no_eh || bb->dfs_index == -1 || (ins->flags & INTERP_INST_FLAG_LIVENESS_MARKER)) current_liveness.ins_index++; +retry_ins: + opcode = ins->opcode; if (MINT_IS_NOP (opcode)) continue; @@ -3801,9 +3805,7 @@ interp_super_instructions (TransformData *td) g_print ("superins: "); interp_dump_ins (ins, td->data_items); } - // The newly added opcode could be part of further superinstructions. Retry - ins = ins->prev; - continue; + goto retry_ins; } } } diff --git a/src/mono/mono/mini/interp/transform-simd.c b/src/mono/mono/mini/interp/transform-simd.c index c1d6b81019d02f..70b88cf0c148d0 100644 --- a/src/mono/mono/mini/interp/transform-simd.c +++ b/src/mono/mono/mini/interp/transform-simd.c @@ -779,6 +779,24 @@ emit_sn_vector4 (TransformData *td, MonoMethod *cmethod, MonoMethodSignature *cs } #if HOST_BROWSER +static MonoTypeEnum +resolve_native_size (MonoTypeEnum type) +{ + if (type == MONO_TYPE_I) +#if TARGET_SIZEOF_VOID_P == 4 + return MONO_TYPE_I4; +#else + return MONO_TYPE_I8; +#endif + else if (type == MONO_TYPE_U) +#if TARGET_SIZEOF_VOID_P == 4 + return MONO_TYPE_U4; +#else + return MONO_TYPE_U8; +#endif + return type; + +} #define PSIMD_ARGTYPE_I1 MONO_TYPE_I1 #define PSIMD_ARGTYPE_I2 MONO_TYPE_I2 @@ -803,6 +821,7 @@ emit_sn_vector4 (TransformData *td, MonoMethod *cmethod, MonoMethodSignature *cs static gboolean packedsimd_type_matches (MonoTypeEnum type, int expected_type) { + type = resolve_native_size (type); if (expected_type == PSIMD_ARGTYPE_ANY) return TRUE; else if (type == expected_type) @@ -906,6 +925,8 @@ lookup_packedsimd_intrinsic (const char *name, MonoType *arg1) arg_type = mono_class_get_context (vector_klass)->class_inst->type_argv [0]; } else if (arg1->type == MONO_TYPE_PTR) { arg_type = arg1->data.type; + } else if (MONO_TYPE_IS_VECTOR_PRIMITIVE(arg1)) { + arg_type = arg1; } else { // g_printf ("%s arg1 type was not pointer or simd type: %s\n", name, m_class_get_name (vector_klass)); return FALSE; @@ -989,7 +1010,13 @@ emit_sri_packedsimd (TransformData *td, MonoMethod *cmethod, MonoMethodSignature int id = lookup_intrins (sri_packedsimd_methods, sizeof (sri_packedsimd_methods), cmethod->name); // We don't early-out for an unrecognized method, we will generate an NIY later - MonoClass *vector_klass = mono_class_from_mono_type_internal (csignature->ret); + MonoClass *vector_klass = NULL; + if (csignature->ret->type == MONO_TYPE_VOID && csignature->param_count > 1 && mono_type_is_pointer (csignature->params [0])) { + // The Store* methods have a more complicated signature + vector_klass = mono_class_from_mono_type_internal (csignature->params [1]); + } else { + vector_klass = mono_class_from_mono_type_internal (csignature->ret); + } MonoTypeEnum atype; int vector_size = -1, arg_size, scalar_arg; diff --git a/src/native/corehost/corehost.cpp b/src/native/corehost/corehost.cpp index c8a94312c5d6d4..c233a52237a9fb 100644 --- a/src/native/corehost/corehost.cpp +++ b/src/native/corehost/corehost.cpp @@ -115,7 +115,7 @@ int exe_start(const int argc, const pal::char_t* argv[]) // Use realpath to find the path of the host, resolving any symlinks. // hostfxr (for dotnet) and the app dll (for apphost) are found relative to the host. pal::string_t host_path; - if (!pal::get_own_executable_path(&host_path) || !pal::realpath(&host_path)) + if (!pal::get_own_executable_path(&host_path) || !pal::fullpath(&host_path)) { trace::error(_X("Failed to resolve full path of the current executable [%s]"), host_path.c_str()); return StatusCode::CoreHostCurHostFindFailure; diff --git a/src/native/corehost/fxr_resolver.cpp b/src/native/corehost/fxr_resolver.cpp index a69bf7ced896a5..0d1cbddf9deb5c 100644 --- a/src/native/corehost/fxr_resolver.cpp +++ b/src/native/corehost/fxr_resolver.cpp @@ -95,7 +95,7 @@ bool fxr_resolver::try_get_path( bool search_global = (search & search_location_global) != 0; pal::string_t default_install_location; pal::string_t dotnet_root_env_var_name; - if (search_app_relative && pal::realpath(app_relative_dotnet_root)) + if (search_app_relative && pal::fullpath(app_relative_dotnet_root)) { trace::info(_X("Using app-relative location [%s] as runtime location."), app_relative_dotnet_root->c_str()); out_dotnet_root->assign(*app_relative_dotnet_root); diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c b/src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c index e444d8c0c57b12..f4a79df5fa1498 100644 --- a/src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c +++ b/src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c @@ -215,6 +215,15 @@ int32_t CryptoNative_RsaEncrypt(EVP_PKEY* pkey, static bool ConfigureSignature(EVP_PKEY_CTX* ctx, RsaPaddingMode padding, const EVP_MD* digest) { + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-qual" + if (EVP_PKEY_CTX_set_signature_md(ctx, digest) <= 0) +#pragma clang diagnostic pop + { + return false; + } + if (padding == RsaPaddingPkcs1) { if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0) @@ -233,14 +242,6 @@ static bool ConfigureSignature(EVP_PKEY_CTX* ctx, RsaPaddingMode padding, const } } -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wcast-qual" - if (EVP_PKEY_CTX_set_signature_md(ctx, digest) <= 0) -#pragma clang diagnostic pop - { - return false; - } - return true; } diff --git a/src/tests/GC/Features/Bridge/Bridge.cs b/src/tests/GC/Features/Bridge/Bridge.cs new file mode 100644 index 00000000000000..c481087943efcd --- /dev/null +++ b/src/tests/GC/Features/Bridge/Bridge.cs @@ -0,0 +1,422 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; + +// False pinning cases are still possible. For example the thread can die +// and its stack reused by another thread. It also seems that a thread that +// does a GC can keep on the stack references to objects it encountered +// during the collection which are never released afterwards. This would +// be more likely to happen with the interpreter which reuses more stack. +public static class FinalizerHelpers +{ + private static IntPtr aptr; + + private static unsafe void NoPinActionHelper(int depth, Action act) + { + // Avoid tail calls + int* values = stackalloc int[20]; + aptr = new IntPtr(values); + + if (depth <= 0) + { + // + // When the action is called, this new thread might have not allocated + // anything yet in the nursery. This means that the address of the first + // object that would be allocated would be at the start of the tlab and + // implicitly the end of the previous tlab (address which can be in use + // when allocating on another thread, at checking if an object fits in + // this other tlab). We allocate a new dummy object to avoid this type + // of false pinning for most common cases. + // + new object(); + act(); + ClearStack(); + } + else + { + NoPinActionHelper(depth - 1, act); + } + } + + private static unsafe void ClearStack() + { + int* values = stackalloc int[25000]; + for (int i = 0; i < 25000; i++) + values[i] = 0; + } + + public static void PerformNoPinAction(Action act) + { + Thread thr = new Thread(() => NoPinActionHelper (128, act)); + thr.Start(); + thr.Join(); + } +} + +public class BridgeBase +{ + public static int fin_count; + + ~BridgeBase() + { + fin_count++; + } +} + +public class Bridge : BridgeBase +{ + public List Links = new List(); + public int __test; + + ~Bridge() + { + Links = null; + } +} + +public class Bridge1 : BridgeBase +{ + public object Link; + ~Bridge1() + { + Link = null; + } +} + +// 128 size +public class Bridge14 : BridgeBase +{ + public object a,b,c,d,e,f,g,h,i,j,k,l,m,n; +} + +public class NonBridge +{ + public object Link; +} + +public class NonBridge2 : NonBridge +{ + public object Link2; +} + +public class NonBridge14 +{ + public object a,b,c,d,e,f,g,h,i,j,k,l,m,n; +} + + +public class BridgeTest +{ + const int OBJ_COUNT = 100 * 1000; + const int LINK_COUNT = 2; + const int EXTRAS_COUNT = 0; + const double survival_rate = 0.1; + + // Pathological case for the original old algorithm. Goes + // away when merging is replaced by appending with flag + // checking. + static void SetupLinks() + { + var list = new List(); + for (int i = 0; i < OBJ_COUNT; ++i) + { + var bridge = new Bridge(); + list.Add(bridge); + } + + var r = new Random(100); + for (int i = 0; i < OBJ_COUNT; ++i) + { + var n = list[i]; + for (int j = 0; j < LINK_COUNT; ++j) + n.Links.Add(list[r.Next (OBJ_COUNT)]); + for (int j = 0; j < EXTRAS_COUNT; ++j) + n.Links.Add(j); + if (r.NextDouble() <= survival_rate) + n.__test = 1; + } + } + + const int LIST_LENGTH = 10000; + const int FAN_OUT = 10000; + + // Pathological case for the new algorithm. Goes away with + // the single-node elimination optimization, but will still + // persist if modified by using a ladder instead of the single + // list. + static void SetupLinkedFan() + { + var head = new Bridge(); + var tail = new NonBridge(); + head.Links.Add(tail); + for (int i = 0; i < LIST_LENGTH; ++i) + { + var obj = new NonBridge (); + tail.Link = obj; + tail = obj; + } + var list = new List(); + tail.Link = list; + for (int i = 0; i < FAN_OUT; ++i) + list.Add (new Bridge()); + } + + // Pathological case for the improved old algorithm. Goes + // away with copy-on-write DynArrays, but will still persist + // if modified by using a ladder instead of the single list. + static void SetupInverseFan() + { + var tail = new Bridge(); + object list = tail; + for (int i = 0; i < LIST_LENGTH; ++i) + { + var obj = new NonBridge(); + obj.Link = list; + list = obj; + } + var heads = new Bridge[FAN_OUT]; + for (int i = 0; i < FAN_OUT; ++i) + { + var obj = new Bridge(); + obj.Links.Add(list); + heads[i] = obj; + } + } + + // Not necessarily a pathology, but a special case of where we + // generate lots of "dead" SCCs. A non-bridge object that + // can't reach a bridge object can safely be removed from the + // graph. In this special case it's a linked list hanging off + // a bridge object. We can handle this by "forwarding" edges + // going to non-bridge nodes that have only a single outgoing + // edge. That collapses the whole list into a single node. + // We could remove that node, too, by removing non-bridge + // nodes with no outgoing edges. + static void SetupDeadList() + { + var head = new Bridge(); + var tail = new NonBridge(); + head.Links.Add(tail); + for (int i = 0; i < LIST_LENGTH; ++i) + { + var obj = new NonBridge(); + tail.Link = obj; + tail = obj; + } + } + + // Triggered a bug in the forwarding mechanic. + static void SetupSelfLinks() + { + var head = new Bridge(); + var tail = new NonBridge(); + head.Links.Add(tail); + tail.Link = tail; + } + + const int L0_COUNT = 50000; + const int L1_COUNT = 50000; + const int EXTRA_LEVELS = 4; + + // Set a complex graph from one bridge to a couple. + // The graph is designed to expose naive coloring on + // tarjan and SCC explosion on classic. + static void Spider() + { + Bridge a = new Bridge(); + Bridge b = new Bridge(); + + var l1 = new List(); + for (int i = 0; i < L0_COUNT; ++i) { + var l0 = new List(); + l0.Add(a); + l0.Add(b); + l1.Add(l0); + } + var last_level = l1; + for (int l = 0; l < EXTRA_LEVELS; ++l) { + int j = 0; + var l2 = new List(); + for (int i = 0; i < L1_COUNT; ++i) { + var tmp = new List(); + tmp.Add(last_level [j++ % last_level.Count]); + tmp.Add(last_level [j++ % last_level.Count]); + l2.Add(tmp); + } + last_level = l2; + } + Bridge c = new Bridge(); + c.Links.Add(last_level); + } + + // Simulates a graph with two nested cycles that is produces by + // the async state machine when `async Task M()` method gets its + // continuation rooted by an Action held by RunnableImplementor + // (ie. the task continuation is hooked through the SynchronizationContext + // implentation and rooted only by Android bridge objects). + static void NestedCycles() + { + Bridge runnableImplementor = new Bridge (); + Bridge byteArrayOutputStream = new Bridge (); + NonBridge2 action = new NonBridge2 (); + NonBridge displayClass = new NonBridge (); + NonBridge2 asyncStateMachineBox = new NonBridge2 (); + NonBridge2 asyncStreamWriter = new NonBridge2 (); + + runnableImplementor.Links.Add(action); + action.Link = displayClass; + action.Link2 = asyncStateMachineBox; + displayClass.Link = action; + asyncStateMachineBox.Link = asyncStreamWriter; + asyncStateMachineBox.Link2 = action; + asyncStreamWriter.Link = byteArrayOutputStream; + asyncStreamWriter.Link2 = asyncStateMachineBox; + } + + // Simulates a graph where a heavy node has its fanout components + // represented by cycles with back-references to the heavy node and + // references to the same bridge objects. + // This enters a pathological path in the SCC contraction where the + // links to the bridge objects need to be correctly deduplicated. The + // deduplication causes the heavy node to no longer be heavy. + static void FauxHeavyNodeWithCycles() + { + Bridge fanout = new Bridge(); + + // Need enough edges for the node to be considered heavy by bridgeless_color_is_heavy + NonBridge[] fauxHeavyNode = new NonBridge[100]; + for (int i = 0; i < fauxHeavyNode.Length; i++) + { + NonBridge2 cycle = new NonBridge2(); + cycle.Link = fanout; + cycle.Link2 = fauxHeavyNode; + fauxHeavyNode[i] = cycle; + } + + // Need at least HEAVY_REFS_MIN + 1 fan-in nodes + Bridge[] faninNodes = new Bridge[3]; + for (int i = 0; i < faninNodes.Length; i++) + { + faninNodes[i] = new Bridge(); + faninNodes[i].Links.Add(fauxHeavyNode); + } + } + + static void RunGraphTest(Action test) + { + Console.WriteLine("Start test {0}", test.Method.Name); + FinalizerHelpers.PerformNoPinAction(test); + Console.WriteLine("-graph built-"); + for (int i = 0; i < 5; i++) + { + Console.WriteLine("-GC {0}/5-", i); + GC.Collect (); + GC.WaitForPendingFinalizers(); + } + + Console.WriteLine("Finished test {0}, finalized {1}", test.Method.Name, Bridge.fin_count); + } + + static void TestLinkedList() + { + int count = Environment.ProcessorCount + 2; + var th = new Thread [count]; + for (int i = 0; i < count; ++i) + { + th [i] = new Thread( _ => + { + var lst = new ArrayList(); + for (var j = 0; j < 500 * 1000; j++) + { + lst.Add (new object()); + if ((j % 999) == 0) + lst.Add (new BridgeBase()); + if ((j % 1000) == 0) + new BridgeBase(); + if ((j % 50000) == 0) + lst = new ArrayList(); + } + }); + + th [i].Start(); + } + + for (int i = 0; i < count; ++i) + th [i].Join(); + + GC.Collect(2); + Console.WriteLine("Finished test LinkedTest, finalized {0}", BridgeBase.fin_count); + } + + //we fill 16Mb worth of stuff, eg, 256k objects + const int major_fill = 1024 * 256; + + //4mb nursery with 64 bytes objects -> alloc half + const int nursery_obj_count = 16 * 1024; + + static void SetupFragmentation() + where TBridge : new() + where TNonBridge : new() + { + const int loops = 4; + for (int k = 0; k < loops; k++) + { + Console.WriteLine("[{0}] CrashLoop {1}/{2}", DateTime.Now, k + 1, loops); + var arr = new object[major_fill]; + for (int i = 0; i < major_fill; i++) + arr[i] = new TNonBridge(); + GC.Collect(1); + Console.WriteLine("[{0}] major fill done", DateTime.Now); + + //induce massive fragmentation + for (int i = 0; i < major_fill; i += 4) + { + arr[i + 1] = null; + arr[i + 2] = null; + arr[i + 3] = null; + } + GC.Collect (1); + Console.WriteLine("[{0}] fragmentation done", DateTime.Now); + + //since 50% is garbage, do 2 fill passes + for (int j = 0; j < 2; ++j) + { + for (int i = 0; i < major_fill; i++) + { + if ((i % 1000) == 0) + new TBridge(); + else + arr[i] = new TBridge(); + } + } + Console.WriteLine("[{0}] done spewing bridges", DateTime.Now); + + for (int i = 0; i < major_fill; i++) + arr[i] = null; + GC.Collect (); + } + } + + public static int Main(string[] args) + { +// TestLinkedList(); // Crashes, but only in this multithreaded variant + RunGraphTest(SetupFragmentation); // This passes but the following crashes ?? +// RunGraphTest(SetupFragmentation); + RunGraphTest(SetupLinks); + RunGraphTest(SetupLinkedFan); + RunGraphTest(SetupInverseFan); + + RunGraphTest(SetupDeadList); + RunGraphTest(SetupSelfLinks); + RunGraphTest(NestedCycles); + RunGraphTest(FauxHeavyNodeWithCycles); +// RunGraphTest(Spider); // Crashes + return 100; + } +} diff --git a/src/tests/GC/Features/Bridge/Bridge.csproj b/src/tests/GC/Features/Bridge/Bridge.csproj new file mode 100644 index 00000000000000..29b8c0f5fd3a2d --- /dev/null +++ b/src/tests/GC/Features/Bridge/Bridge.csproj @@ -0,0 +1,11 @@ + + + + true + false + BuildOnly + + + + + diff --git a/src/tests/GC/Features/Bridge/BridgeTester.cs b/src/tests/GC/Features/Bridge/BridgeTester.cs new file mode 100644 index 00000000000000..960e39a5e6eb0d --- /dev/null +++ b/src/tests/GC/Features/Bridge/BridgeTester.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime; +using System.Text; + +using Xunit; + +public class BridgeTester +{ + [Fact] + public static void RunTests() + { + string corerun = TestLibrary.Utilities.IsWindows ? "corerun.exe" : "corerun"; + string coreRoot = Environment.GetEnvironmentVariable("CORE_ROOT"); + string bridgeTestApp = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Bridge.dll"); + + var startInfo = new ProcessStartInfo(Path.Combine(coreRoot, corerun), bridgeTestApp); + startInfo.EnvironmentVariables["MONO_GC_DEBUG"] = "bridge=BridgeBase,bridge-compare-to=new"; + startInfo.EnvironmentVariables["MONO_GC_PARAMS"] = "bridge-implementation=tarjan,bridge-require-precise-merge"; + + using (Process p = Process.Start(startInfo)) + { + p.WaitForExit(); + Console.WriteLine ("Bridge Test App returned {0}", p.ExitCode); + if (p.ExitCode != 100) + throw new Exception("Bridge Test App failed execution"); + } + } +} diff --git a/src/tests/GC/Features/Bridge/BridgeTester.csproj b/src/tests/GC/Features/Bridge/BridgeTester.csproj new file mode 100644 index 00000000000000..5045d91e4c89a8 --- /dev/null +++ b/src/tests/GC/Features/Bridge/BridgeTester.csproj @@ -0,0 +1,17 @@ + + + true + true + + + + + + + + false + Content + Always + + + diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_113658/Runtime_113658.cs b/src/tests/JIT/Regression/JitBlue/Runtime_113658/Runtime_113658.cs new file mode 100644 index 00000000000000..089f666d280002 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_113658/Runtime_113658.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using Xunit; + +public static class Runtime_113658 +{ + [Fact] + public static int TestEntryPoint() + { + FillStackWithGarbage(); + long? nullable = FaultyDefaultNullable(); + return (int)(100 + nullable.GetValueOrDefault()); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void FillStackWithGarbage() + { + stackalloc byte[256].Fill(0xcc); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + [SkipLocalsInit] + private static T FaultyDefaultNullable() + { + // When T is a Nullable (and only in that case), we support returning null + if (default(T) is null && typeof(T).IsValueType) + return default!; + + throw new InvalidOperationException("Not nullable"); + } +} diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_113658/Runtime_113658.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_113658/Runtime_113658.csproj new file mode 100644 index 00000000000000..de6d5e08882e86 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_113658/Runtime_113658.csproj @@ -0,0 +1,8 @@ + + + True + + + + + diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_115815/Runtime_115815.cs b/src/tests/JIT/Regression/JitBlue/Runtime_115815/Runtime_115815.cs new file mode 100644 index 00000000000000..77f42fe7d49453 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_115815/Runtime_115815.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Xunit; + +public class Runtime_115815 +{ + [Fact] + public static void TestEntryPoint() + { + var destination = new KeyValuePair[1_000]; + + // loop to make this method fully interruptible + to get into OSR version + for (int i = 0; i < destination.Length * 1000; i++) + { + destination[i / 1000] = default; + } + + for (int i = 0; i < 5; i++) + { + for (int j = 0; j < destination.Length; j++) + { + destination[j] = GetValue(j); + } + + Thread.Sleep(10); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static KeyValuePair GetValue(int i) + => KeyValuePair.Create(new Container(i.ToString()), (double)i); + + private struct Container + { + public string Name; + public Container(string name) { this.Name = name; } + } +} diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_115815/Runtime_115815.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_115815/Runtime_115815.csproj new file mode 100644 index 00000000000000..de6d5e08882e86 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_115815/Runtime_115815.csproj @@ -0,0 +1,8 @@ + + + True + + + + +