summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam Godbe <wigodbe@microsoft.com>2019-03-26 10:59:04 -0700
committerGitHub <noreply@github.com>2019-03-26 10:59:04 -0700
commit9efba4fab6ef4401b431d0ec8c2943bcfc81b2b1 (patch)
tree9ca64fd3300c29f128091cedbf7a5f1753bb17c5
parent0d8c82a7db5123f1da1f639e4a032b8c8c0aa139 (diff)
parente6c49f73eac2413fdc7a1024fc0f1585e39db026 (diff)
downloadcoreclr-9efba4fab6ef4401b431d0ec8c2943bcfc81b2b1.tar.gz
coreclr-9efba4fab6ef4401b431d0ec8c2943bcfc81b2b1.tar.bz2
coreclr-9efba4fab6ef4401b431d0ec8c2943bcfc81b2b1.zip
Merge pull request #23433 from dotnet-maestro-bot/merge/master-to-release/3.0
[automated] Merge branch 'master' => 'release/3.0'
-rw-r--r--Documentation/Profiling/Profiler Breaking Changes.md6
-rw-r--r--Documentation/design-docs/assemblyloadcontext.md2
-rw-r--r--azure-pipelines.yml27
-rwxr-xr-xbuild-test.sh41
-rw-r--r--build.cmd4
-rwxr-xr-xbuild.sh23
-rw-r--r--clr.defines.targets1
-rw-r--r--clrdefinitions.cmake1
-rw-r--r--configurecompiler.cmake1
-rw-r--r--eng/Version.Details.xml12
-rw-r--r--eng/Versions.props2
-rw-r--r--eng/build-job.yml7
-rw-r--r--eng/common/PublishToPackageFeed.proj24
-rw-r--r--eng/common/generate-graph-files.ps146
-rw-r--r--eng/common/init-tools-native.ps114
-rw-r--r--eng/common/templates/job/publish-build-assets.yml4
-rw-r--r--eng/common/templates/jobs/jobs.yml4
-rw-r--r--eng/common/templates/steps/send-to-helix.yml5
-rw-r--r--eng/kill_tasks.cmd10
-rw-r--r--eng/platform-matrix.yml22
-rw-r--r--global.json4
-rwxr-xr-xnetci.groovy100
-rw-r--r--perf.groovy6
-rw-r--r--src/System.Private.CoreLib/System.Private.CoreLib.csproj16
-rw-r--r--src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems5
-rw-r--r--src/System.Private.CoreLib/shared/System/Buffer.Unix.cs26
-rw-r--r--src/System.Private.CoreLib/shared/System/Buffer.Windows.cs23
-rw-r--r--src/System.Private.CoreLib/shared/System/Buffer.cs42
-rw-r--r--src/System.Private.CoreLib/shared/System/Collections/ObjectModel/Collection.cs14
-rw-r--r--src/System.Private.CoreLib/shared/System/CurrentSystemTimeZone.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerBrowsableAttribute.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/ActivityTracker.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs6
-rw-r--r--src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingEventCounter.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingPollingCounter.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/PollingCounter.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Enum.cs118
-rw-r--r--src/System.Private.CoreLib/shared/System/Exception.cs196
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/CultureInfo.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/DateTimeParse.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/EastAsianLunisolarCalendar.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/HebrewCalendar.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/IO/FileStream.Win32.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Index.cs12
-rw-r--r--src/System.Private.CoreLib/shared/System/Memory.cs32
-rw-r--r--src/System.Private.CoreLib/shared/System/MemoryExtensions.Trim.cs866
-rw-r--r--src/System.Private.CoreLib/shared/System/MemoryExtensions.cs886
-rw-r--r--src/System.Private.CoreLib/shared/System/Nullable.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs32
-rw-r--r--src/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs14
-rw-r--r--src/System.Private.CoreLib/shared/System/Resources/RuntimeResourceSet.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs7
-rw-r--r--src/System.Private.CoreLib/shared/System/Security/SecurityElement.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Span.Fast.cs14
-rw-r--r--src/System.Private.CoreLib/shared/System/String.cs13
-rw-r--r--src/System.Private.CoreLib/shared/System/Text/ASCIIUtility.cs1818
-rw-r--r--src/System.Private.CoreLib/shared/System/Text/DecoderFallback.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Text/DecoderNLS.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Text/Encoding.Internal.cs4
-rw-r--r--src/System.Private.CoreLib/shared/System/Text/Rune.cs283
-rw-r--r--src/System.Private.CoreLib/shared/System/Text/UTF8Encoding.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Text/Unicode/Utf8.cs8
-rw-r--r--src/System.Private.CoreLib/shared/System/Text/Unicode/Utf8Utility.cs106
-rw-r--r--src/System.Private.CoreLib/shared/System/Text/UnicodeUtility.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Threading/SpinWait.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/Threading/Tasks/Sources/ManualResetValueTaskSourceCore.cs2
-rw-r--r--src/System.Private.CoreLib/shared/System/ThrowHelper.cs2
-rw-r--r--src/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs34
-rw-r--r--src/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs32
-rw-r--r--src/System.Private.CoreLib/src/Internal/Runtime/InteropServices/IsolatedComponentLoadContext.cs47
-rw-r--r--src/System.Private.CoreLib/src/System/Char8.cs69
-rw-r--r--src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.cs12
-rw-r--r--src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSource.cs97
-rw-r--r--src/System.Private.CoreLib/src/System/Enum.CoreCLR.cs75
-rw-r--r--src/System.Private.CoreLib/src/System/Exception.CoreCLR.cs (renamed from src/System.Private.CoreLib/src/System/Exception.cs)323
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs13
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs24
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeMarshal.cs33
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CoreCLR.cs28
-rw-r--r--src/System.Private.CoreLib/src/System/Utf8Extensions.cs367
-rw-r--r--src/System.Private.CoreLib/src/System/Utf8String.Construction.cs223
-rw-r--r--src/System.Private.CoreLib/src/System/Utf8String.Manipulation.cs109
-rw-r--r--src/System.Private.CoreLib/src/System/Utf8String.Searching.cs93
-rw-r--r--src/System.Private.CoreLib/src/System/Utf8String.cs252
-rw-r--r--src/classlibnative/bcltype/objectnative.cpp3
-rw-r--r--src/debug/daccess/daccess.cpp4
-rw-r--r--src/debug/debug-pal/CMakeLists.txt6
-rw-r--r--src/debug/debug-pal/unix/diagnosticsipc.cpp181
-rw-r--r--src/debug/debug-pal/win/diagnosticsipc.cpp146
-rw-r--r--src/debug/ee/debugger.cpp2
-rw-r--r--src/debug/inc/diagnosticsipc.h79
-rw-r--r--src/dlls/mscorrc/mscorrc.rc2
-rw-r--r--src/dlls/mscorrc/resource.h2
-rw-r--r--src/gc/gc.cpp8
-rw-r--r--src/gc/gcpriv.h2
-rw-r--r--src/gc/sample/gcenv.ee.cpp4
-rw-r--r--src/gc/unix/cgroup.cpp13
-rw-r--r--src/gc/unix/gcenv.unix.cpp2
-rw-r--r--src/gc/windows/gcenv.windows.cpp21
-rw-r--r--src/inc/dacvars.h3
-rw-r--r--src/inc/loglf.h2
-rw-r--r--src/inc/stresslog.h2
-rw-r--r--src/jit/CMakeLists.txt5
-rw-r--r--src/jit/assertionprop.cpp13
-rw-r--r--src/jit/bitset.h2
-rw-r--r--src/jit/bitsetasshortlong.h5
-rw-r--r--src/jit/block.cpp140
-rw-r--r--src/jit/block.h2
-rw-r--r--src/jit/codegen.h24
-rw-r--r--src/jit/codegenarm64.cpp14
-rw-r--r--src/jit/codegenarmarch.cpp3
-rw-r--r--src/jit/codegencommon.cpp93
-rw-r--r--src/jit/codegeninterface.h31
-rw-r--r--src/jit/codegenlinear.cpp21
-rw-r--r--src/jit/codegenxarch.cpp11
-rw-r--r--src/jit/compiler.cpp136
-rw-r--r--src/jit/compiler.h12
-rw-r--r--src/jit/compiler.hpp2
-rw-r--r--src/jit/earlyprop.cpp11
-rw-r--r--src/jit/ee_il_dll.cpp9
-rw-r--r--src/jit/emit.cpp225
-rw-r--r--src/jit/emit.h144
-rw-r--r--src/jit/emitarm.cpp4
-rw-r--r--src/jit/emitarm64.cpp2
-rw-r--r--src/jit/emitxarch.cpp28
-rw-r--r--src/jit/flowgraph.cpp22
-rw-r--r--src/jit/gcencode.cpp2
-rw-r--r--src/jit/gcinfo.cpp2
-rw-r--r--src/jit/gschecks.cpp1
-rw-r--r--src/jit/gtlist.h138
-rw-r--r--src/jit/hashbv.cpp14
-rw-r--r--src/jit/importer.cpp32
-rw-r--r--src/jit/jiteh.cpp7
-rw-r--r--src/jit/jitstd/hashtable.h1
-rw-r--r--src/jit/lclvars.cpp16
-rw-r--r--src/jit/liveness.cpp2
-rw-r--r--src/jit/loopcloning.h2
-rw-r--r--src/jit/morph.cpp76
-rw-r--r--src/jit/optimizer.cpp2
-rw-r--r--src/jit/regset.h2
-rw-r--r--src/jit/scopeinfo.cpp172
-rw-r--r--src/jit/treelifeupdater.cpp3
-rw-r--r--src/jit/unwindarm.cpp2
-rw-r--r--src/jit/utils.cpp6
-rw-r--r--src/nativeresources/rctocpp.awk4
-rw-r--r--src/pal/automation/automation.py78
-rw-r--r--src/pal/automation/compile.py70
-rw-r--r--src/pal/automation/tests.py33
-rw-r--r--src/pal/automation/util.py97
-rw-r--r--src/pal/inc/pal.h13
-rw-r--r--src/pal/src/misc/cgroup.cpp18
-rw-r--r--src/pal/src/misc/sysinfo.cpp12
-rw-r--r--src/pal/src/numa/numa.cpp2
-rw-r--r--src/pal/src/thread/process.cpp54
-rwxr-xr-xsrc/pal/tools/gen-buildsys-clang.sh13
-rwxr-xr-xsrc/pal/tools/gen-buildsys-gcc.sh11
-rw-r--r--src/scripts/genEtwProvider.py53
-rw-r--r--src/scripts/genEventPipe.py29
-rw-r--r--src/scripts/utilities.py46
-rw-r--r--src/strongname/api/common.h3
-rw-r--r--src/tools/r2rdump/DisassemblingTypeProvider.cs55
-rw-r--r--src/tools/r2rdump/R2RDump.cs2
-rw-r--r--src/tools/r2rdump/R2RReader.cs12
-rw-r--r--src/tools/r2rdump/R2RSignature.cs43
-rw-r--r--src/vm/CMakeLists.txt9
-rw-r--r--src/vm/appdomain.cpp7
-rw-r--r--src/vm/assemblynative.cpp36
-rw-r--r--src/vm/assemblynative.hpp3
-rw-r--r--src/vm/ceeload.cpp134
-rw-r--r--src/vm/ceeload.h7
-rw-r--r--src/vm/ceemain.cpp3
-rw-r--r--src/vm/classnames.h4
-rw-r--r--src/vm/clsload.cpp26
-rw-r--r--src/vm/comdelegate.cpp78
-rw-r--r--src/vm/common.h4
-rw-r--r--src/vm/diagnosticserver.cpp181
-rw-r--r--src/vm/diagnosticserver.h62
-rw-r--r--src/vm/diagnosticsprotocol.h54
-rw-r--r--src/vm/dllimport.cpp5
-rw-r--r--src/vm/domainfile.cpp8
-rw-r--r--src/vm/domainfile.h2
-rw-r--r--src/vm/ecall.cpp70
-rw-r--r--src/vm/ecall.h18
-rw-r--r--src/vm/ecalllist.h36
-rw-r--r--src/vm/eventpipe.cpp428
-rw-r--r--src/vm/eventpipe.h326
-rw-r--r--src/vm/eventpipeconfiguration.cpp120
-rw-r--r--src/vm/eventpipeconfiguration.h25
-rw-r--r--src/vm/eventpipeeventinstance.h2
-rw-r--r--src/vm/eventpipeeventsource.cpp2
-rw-r--r--src/vm/eventpipefile.cpp9
-rw-r--r--src/vm/eventpipeinternal.cpp270
-rw-r--r--src/vm/eventpipeinternal.h107
-rw-r--r--src/vm/eventpipeprotocolhelper.cpp156
-rw-r--r--src/vm/eventpipeprotocolhelper.h35
-rw-r--r--src/vm/eventpipeprovider.cpp13
-rw-r--r--src/vm/eventpipeprovider.h11
-rw-r--r--src/vm/eventpipesession.cpp226
-rw-r--r--src/vm/eventpipesession.h74
-rw-r--r--src/vm/eventpipesessionprovider.cpp195
-rw-r--r--src/vm/eventpipesessionprovider.h81
-rw-r--r--src/vm/fastserializer.cpp126
-rw-r--r--src/vm/fastserializer.h70
-rw-r--r--src/vm/gcenv.os.cpp21
-rw-r--r--src/vm/gchandleutilities.cpp2
-rw-r--r--src/vm/gchandleutilities.h2
-rw-r--r--src/vm/gchelpers.cpp77
-rw-r--r--src/vm/gchelpers.h17
-rw-r--r--src/vm/gdbjit.cpp4
-rw-r--r--src/vm/ilmarshalers.cpp86
-rw-r--r--src/vm/ilmarshalers.h32
-rw-r--r--src/vm/jithelpers.cpp71
-rw-r--r--src/vm/jitinterface.cpp50
-rw-r--r--src/vm/jitinterface.h5
-rw-r--r--src/vm/jitinterfacegen.cpp9
-rw-r--r--src/vm/marshalnative.cpp32
-rw-r--r--src/vm/marshalnative.h5
-rw-r--r--src/vm/metasig.h14
-rw-r--r--src/vm/methodtable.h2
-rw-r--r--src/vm/methodtablebuilder.cpp13
-rw-r--r--src/vm/mlinfo.cpp74
-rw-r--r--src/vm/mscorlib.cpp2
-rw-r--r--src/vm/mscorlib.h24
-rw-r--r--src/vm/mtypes.h2
-rw-r--r--src/vm/object.h77
-rw-r--r--src/vm/object.inl16
-rw-r--r--src/vm/proftoeeinterfaceimpl.cpp4
-rw-r--r--src/vm/reflectioninvocation.cpp18
-rw-r--r--src/vm/threadpoolrequest.h4
-rw-r--r--src/vm/vars.cpp3
-rw-r--r--src/vm/vars.hpp14
-rw-r--r--src/zap/zapimport.cpp8
-rw-r--r--tests/CoreFX/CoreFX.issues.json4
-rw-r--r--tests/arm64/corefx_test_exclusions.txt11
-rw-r--r--tests/issues.targets14
-rw-r--r--tests/src/GC/API/GC/Collect.csproj1
-rw-r--r--tests/src/Interop/CMakeLists.txt7
-rw-r--r--tests/src/Interop/IJW/CopyConstructorMarshaler/CMakeLists.txt42
-rw-r--r--tests/src/Interop/IJW/CopyConstructorMarshaler/CopyConstructorMarshaler.cs66
-rw-r--r--tests/src/Interop/IJW/CopyConstructorMarshaler/CopyConstructorMarshaler.csproj44
-rw-r--r--tests/src/Interop/IJW/CopyConstructorMarshaler/IjwCopyConstructorMarshaler.cpp106
-rw-r--r--tests/src/Interop/IJW/FakeMscoree/mscoree.cpp19
-rw-r--r--tests/src/Interop/IJW/FakeMscoree/mscoree.def3
-rw-r--r--tests/src/Interop/IJW/FixupCallsHostWhenLoaded/FixupCallsHostWhenLoaded.cs53
-rw-r--r--tests/src/Interop/IJW/FixupCallsHostWhenLoaded/FixupCallsHostWhenLoaded.csproj44
-rw-r--r--tests/src/Interop/IJW/IjwNativeCallingManagedDll/CMakeLists.txt (renamed from tests/src/Interop/IJW/NativeCallingManaged/IjwNativeCallingManagedDll/CMakeLists.txt)0
-rw-r--r--tests/src/Interop/IJW/IjwNativeCallingManagedDll/IjwNativeCallingManagedDll.cpp (renamed from tests/src/Interop/IJW/NativeCallingManaged/IjwNativeCallingManagedDll/IjwNativeCallingManagedDll.cpp)28
-rw-r--r--tests/src/Interop/IJW/IjwNativeDll/CMakeLists.txt (renamed from tests/src/Interop/IJW/ManagedCallingNative/IjwNativeDll/CMakeLists.txt)0
-rw-r--r--tests/src/Interop/IJW/IjwNativeDll/IjwNativeDll.cpp (renamed from tests/src/Interop/IJW/ManagedCallingNative/IjwNativeDll/IjwNativeDll.cpp)0
-rw-r--r--tests/src/Interop/IJW/LoadIjwFromModuleHandle/LoadIjwFromModuleHandle.cs88
-rw-r--r--tests/src/Interop/IJW/LoadIjwFromModuleHandle/LoadIjwFromModuleHandle.csproj47
-rw-r--r--tests/src/Interop/IJW/ManagedCallingNative/ManagedCallingNative.csproj4
-rw-r--r--tests/src/Interop/IJW/NativeCallingManaged/NativeCallingManaged.csproj4
-rw-r--r--tests/src/Interop/IJW/ijwhostmock/CMakeLists.txt (renamed from tests/src/Interop/IJW/FakeMscoree/CMakeLists.txt)2
-rw-r--r--tests/src/Interop/IJW/ijwhostmock/mscoree.cpp34
-rw-r--r--tests/src/Interop/SizeConst/SizeConstTest.cs2
-rw-r--r--tests/src/JIT/Directed/VectorABI/VectorMgdMgd.cs1063
-rw-r--r--tests/src/JIT/Regression/JitBlue/GitHub_18582/GitHub_18582.cs221
-rw-r--r--tests/src/JIT/Regression/JitBlue/GitHub_18582/GitHub_18582.csproj (renamed from tests/src/Regressions/coreclr/22728/debug15.ilproj)30
-rw-r--r--tests/src/Regressions/coreclr/22728/createdelegate.il103
-rw-r--r--tests/src/Regressions/coreclr/22728/debug15.il133
-rw-r--r--tests/src/tracing/eventcounter/pollingcounter.cs192
-rw-r--r--tests/src/tracing/eventcounter/pollingcounter.csproj (renamed from tests/src/Regressions/coreclr/22728/createdelegate.ilproj)35
263 files changed, 11506 insertions, 3954 deletions
diff --git a/Documentation/Profiling/Profiler Breaking Changes.md b/Documentation/Profiling/Profiler Breaking Changes.md
new file mode 100644
index 0000000000..f63b22764e
--- /dev/null
+++ b/Documentation/Profiling/Profiler Breaking Changes.md
@@ -0,0 +1,6 @@
+# Profiler Breaking Changes #
+
+Over time we will need to modify the Profiler APIs, this document will serve as a record of any breaking changes.
+
+1. Code Versioning introduced changes documented [here](../design-docs/code-versioning-profiler-breaking-changes.md)
+2. The work to allow adding new types and methods after module load means ICorProfilerInfo7::ApplyMetadata will now potentially trigger a GC, and will not be callable in situations where a GC can not happen (for example ICorProfilerCallback::RootReferences). \ No newline at end of file
diff --git a/Documentation/design-docs/assemblyloadcontext.md b/Documentation/design-docs/assemblyloadcontext.md
index c9575c4be6..6f601f79cb 100644
--- a/Documentation/design-docs/assemblyloadcontext.md
+++ b/Documentation/design-docs/assemblyloadcontext.md
@@ -64,7 +64,7 @@ This property will return a reference to the *Default LoadContext*.
### Load
-This method should be overriden in a *Custom LoadContext* if the intent is to override the assembly resolution that would be done during fallback to *Defaut LoadContext*
+This method should be overriden in a *Custom LoadContext* if the intent is to override the assembly resolution that would be done during fallback to *Default LoadContext*
### LoadFromAssemblyName
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index b1ef2dac13..f590362601 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -112,6 +112,13 @@ jobs:
parameters:
jobTemplate: build-job.yml
buildConfig: checked
+ ${{ if eq(variables['Build.DefinitionName'], 'coreclr-outerloop-gcstress0x3-gcstress0xc') }}:
+ platforms:
+ - Linux_arm
+ - Linux_arm64
+ - Linux_x64
+ - Windows_NT_x64
+ - Windows_NT_x86
#
# Release build (Pull request)
@@ -146,6 +153,13 @@ jobs:
parameters:
jobTemplate: test-job.yml
buildConfig: checked
+ ${{ if eq(variables['Build.DefinitionName'], 'coreclr-outerloop-gcstress0x3-gcstress0xc') }}:
+ platforms:
+ - Linux_arm
+ - Linux_arm64
+ - Linux_x64
+ - Windows_NT_x64
+ - Windows_NT_x86
jobParameters:
${{ if eq(variables['Build.DefinitionName'], 'coreclr-ci') }}:
testGroup: innerloop
@@ -181,7 +195,18 @@ jobs:
# Release test builds
#
-# Official build
+# Pull-Request test
+- ${{ if and(eq(variables['System.TeamProject'], 'public'), eq(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.DefinitionName'], 'coreclr-ci')) }}:
+ - template: eng/platform-matrix.yml
+ parameters:
+ jobTemplate: test-job.yml
+ buildConfig: release
+ platforms:
+ - Linux_musl_x64
+ jobParameters:
+ testGroup: innerloop
+
+# Official test
- ${{ if and(eq(variables['System.TeamProject'], 'internal'), ne(variables['Build.Reason'], 'PullRequest')) }}:
- template: eng/platform-matrix.yml
parameters:
diff --git a/build-test.sh b/build-test.sh
index fe86703f56..d845185bcd 100755
--- a/build-test.sh
+++ b/build-test.sh
@@ -436,7 +436,12 @@ build_native_projects()
pushd "$intermediatesForBuild"
# Regenerate the CMake solution
# Force cross dir to point to project root cross dir, in case there is a cross build.
- nextCommand="CONFIG_DIR=\"$__ProjectRoot/cross\" \"$__ProjectRoot/src/pal/tools/gen-buildsys-clang.sh\" \"$__TestDir\" $__ClangMajorVersion $__ClangMinorVersion $platformArch $__BuildType $__CodeCoverage $generator $extraCmakeArguments $__cmakeargs"
+ scriptDir="$__ProjectRoot/src/pal/tools"
+ if [[ $__GccBuild == 0 ]]; then
+ nextCommand="CONFIG_DIR=\"$__ProjectRoot/cross\" \"$scriptDir/gen-buildsys-clang.sh\" \"$__TestDir\" $__ClangMajorVersion $__ClangMinorVersion $platformArch $scriptDir $__BuildType $__CodeCoverage $generator $extraCmakeArguments $__cmakeargs"
+ else
+ nextCommand="CONFIG_DIR=\"$__ProjectRoot/cross\" \"$scriptDir/gen-buildsys-gcc.sh\" \"$__TestDir\" \"$__GccMajorVersion\" \"$__GccMinorVersion\" $platformArch $scriptDir $__BuildType $__CodeCoverage $generator $extraCmakeArguments $__cmakeargs"
+ fi
echo "Invoking $nextCommand"
eval $nextCommand
popd
@@ -475,6 +480,7 @@ usage()
echo "coverage - optional argument to enable code coverage build (currently supported only for Linux and OSX)."
echo "ninja - target ninja instead of GNU make"
echo "clangx.y - optional argument to build using clang version x.y - supported version 3.5 - 6.0"
+ echo "gccx.y - optional argument to build using gcc version x.y."
echo "cross - optional argument to signify cross compilation,"
echo " - will use ROOTFS_DIR environment variable if set."
echo "portableLinux - build for Portable Linux Distribution"
@@ -601,6 +607,9 @@ __ConfigureOnly=0
__CrossBuild=0
__ClangMajorVersion=0
__ClangMinorVersion=0
+__GccBuild=0
+__GccMajorVersion=0
+__GccMinorVersion=0
__NuGetPath="$__PackagesDir/NuGet.exe"
__SkipRestorePackages=0
__DistroRid=""
@@ -726,6 +735,36 @@ while :; do
__ClangMinorVersion=0
;;
+ gcc5|-gcc5)
+ __GccMajorVersion=5
+ __GccMinorVersion=
+ __GccBuild=1
+ ;;
+
+ gcc6|-gcc6)
+ __GccMajorVersion=6
+ __GccMinorVersion=
+ __GccBuild=1
+ ;;
+
+ gcc7|-gcc7)
+ __GccMajorVersion=7
+ __GccMinorVersion=
+ __GccBuild=1
+ ;;
+
+ gcc8|-gcc8)
+ __GccMajorVersion=8
+ __GccMinorVersion=
+ __GccBuild=1
+ ;;
+
+ gcc|-gcc)
+ __GccMajorVersion=
+ __GccMinorVersion=
+ __GccBuild=1
+ ;;
+
ninja)
__UseNinja=1
;;
diff --git a/build.cmd b/build.cmd
index 3617b1b48a..1502783ab9 100644
--- a/build.cmd
+++ b/build.cmd
@@ -426,7 +426,7 @@ if %__BuildNative% EQU 1 (
"!PYTHON!" -B -Wall %__SourceDir%\scripts\genEventing.py --inc %__IntermediatesIncDir% --dummy %__IntermediatesIncDir%\etmdummy.h --man %__SourceDir%\vm\ClrEtwAll.man --nonextern --noxplatheader|| exit /b 1
echo %__MsgPrefix%Laying out dynamically generated EventPipe Implementation
- "!PYTHON!" -B -Wall %__SourceDir%\scripts\genEventPipe.py --man %__SourceDir%\vm\ClrEtwAll.man --intermediate %__IntermediatesEventingDir%\eventpipe --nonextern || exit /b 1
+ "!PYTHON!" -B -Wall %__SourceDir%\scripts\genEventPipe.py --man %__SourceDir%\vm\ClrEtwAll.man --exc %__SourceDir%\vm\ClrEtwAllMeta.lst --intermediate %__IntermediatesEventingDir%\eventpipe --nonextern || exit /b 1
echo %__MsgPrefix%Laying out ETW event logging interface
"!PYTHON!" -B -Wall %__SourceDir%\scripts\genEtwProvider.py --man %__SourceDir%\vm\ClrEtwAll.man --intermediate %__IntermediatesIncDir% --exc %__SourceDir%\vm\ClrEtwAllMeta.lst || exit /b 1
@@ -448,7 +448,7 @@ if %__BuildCrossArchNative% EQU 1 (
"!PYTHON!" -B -Wall %__SourceDir%\scripts\genEventing.py --inc !__CrossCompIntermediatesIncDir! --dummy !__CrossCompIntermediatesIncDir!\etmdummy.h --man %__SourceDir%\vm\ClrEtwAll.man --nonextern || exit /b 1
echo %__MsgPrefix%Laying out dynamically generated EventPipe Implementation
- "!PYTHON!" -B -Wall %__SourceDir%\scripts\genEventPipe.py --man %__SourceDir%\vm\ClrEtwAll.man --intermediate !__CrossCompIntermediatesEventingDir!\eventpipe --nonextern || exit /b 1
+ "!PYTHON!" -B -Wall %__SourceDir%\scripts\genEventPipe.py --man %__SourceDir%\vm\ClrEtwAll.man --exc %__SourceDir%\vm\ClrEtwAllMeta.lst --intermediate !__CrossCompIntermediatesEventingDir!\eventpipe --nonextern || exit /b 1
echo %__MsgPrefix%Laying out dynamically generated EventSource classes
"!PYTHON!" -B -Wall %__SourceDir%\scripts\genRuntimeEventSources.py --man %__SourceDir%\vm\ClrEtwAll.man --intermediate !__CrossCompIntermediatesEventingDir! || exit /b 1
diff --git a/build.sh b/build.sh
index 71b07fa2e8..a24bd89070 100755
--- a/build.sh
+++ b/build.sh
@@ -188,7 +188,7 @@ generate_event_logging_sources()
fi
echo "Laying out dynamically generated EventPipe Implementation"
- $PYTHON -B $__PythonWarningFlags "$__ProjectRoot/src/scripts/genEventPipe.py" --man "$__ProjectRoot/src/vm/ClrEtwAll.man" --intermediate "$__OutputEventingDir/eventpipe"
+ $PYTHON -B $__PythonWarningFlags "$__ProjectRoot/src/scripts/genEventPipe.py" --man "$__ProjectRoot/src/vm/ClrEtwAll.man" --exc "$__ProjectRoot/src/vm/ClrEtwAllMeta.lst" --intermediate "$__OutputEventingDir/eventpipe"
echo "Laying out dynamically generated EventSource classes"
$PYTHON -B $__PythonWarningFlags "$__ProjectRoot/src/scripts/genRuntimeEventSources.py" --man "$__ProjectRoot/src/vm/ClrEtwAll.man" --intermediate "$__OutputEventingDir"
@@ -276,16 +276,17 @@ build_native()
pushd "$intermediatesForBuild"
# Regenerate the CMake solution
+ scriptDir="$__ProjectRoot/src/pal/tools"
if [[ $__GccBuild == 0 ]]; then
scan_build=
if [[ $__StaticAnalyzer == 1 ]]; then
scan_build=scan-build
fi
- echo "Invoking \"$__ProjectRoot/src/pal/tools/gen-buildsys-clang.sh\" \"$__ProjectRoot\" $__ClangMajorVersion \"$__ClangMinorVersion\" $platformArch $__BuildType $__CodeCoverage $scan_build $generator $extraCmakeArguments $__cmakeargs"
- source "$__ProjectRoot/src/pal/tools/gen-buildsys-clang.sh" "$__ProjectRoot" $__ClangMajorVersion "$__ClangMinorVersion" $platformArch $__BuildType $__CodeCoverage $scan_build $generator "$extraCmakeArguments" "$__cmakeargs"
+ echo "Invoking \"$scriptDir/gen-buildsys-clang.sh\" \"$__ProjectRoot\" $__ClangMajorVersion \"$__ClangMinorVersion\" $platformArch "$scriptDir" $__BuildType $__CodeCoverage $scan_build $generator $extraCmakeArguments $__cmakeargs"
+ source "$scriptDir/gen-buildsys-clang.sh" "$__ProjectRoot" $__ClangMajorVersion "$__ClangMinorVersion" $platformArch "$scriptDir" $__BuildType $__CodeCoverage $scan_build $generator "$extraCmakeArguments" "$__cmakeargs"
else
- echo "Invoking \"$__ProjectRoot/src/pal/tools/gen-buildsys-gcc.sh\" \"$__ProjectRoot\" $__GccMajorVersion \"$__GccMinorVersion\" $platformArch $__BuildType $__CodeCoverage $generator $extraCmakeArguments $__cmakeargs"
- source "$__ProjectRoot/src/pal/tools/gen-buildsys-gcc.sh" "$__ProjectRoot" "$__GccMajorVersion" "$__CGccMinorVersion" $platformArch $__BuildType $__CodeCoverage $generator "$extraCmakeArguments" "$__cmakeargs"
+ echo "Invoking \"$scriptDir/gen-buildsys-gcc.sh\" \"$__ProjectRoot\" $__GccMajorVersion \"$__GccMinorVersion\" $platformArch "$scriptDir" $__BuildType $__CodeCoverage $generator $extraCmakeArguments $__cmakeargs"
+ source "$scriptDir/gen-buildsys-gcc.sh" "$__ProjectRoot" "$__GccMajorVersion" "$__CGccMinorVersion" $platformArch "$scriptDir" $__BuildType $__CodeCoverage $generator "$extraCmakeArguments" "$__cmakeargs"
fi
popd
fi
@@ -788,12 +789,24 @@ while :; do
__GccBuild=1
;;
+ gcc6|-gcc6)
+ __GccMajorVersion=6
+ __GccMinorVersion=
+ __GccBuild=1
+ ;;
+
gcc7|-gcc7)
__GccMajorVersion=7
__GccMinorVersion=
__GccBuild=1
;;
+ gcc8|-gcc8)
+ __GccMajorVersion=8
+ __GccMinorVersion=
+ __GccBuild=1
+ ;;
+
gcc|-gcc)
__GccMajorVersion=
__GccMinorVersion=
diff --git a/clr.defines.targets b/clr.defines.targets
index 3fa0417f51..e2f10586f1 100644
--- a/clr.defines.targets
+++ b/clr.defines.targets
@@ -1,6 +1,7 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Features we're currently flighting, but don't intend to ship in officially supported releases -->
<PropertyGroup Condition="'$(IsPrerelease)' == 'true'">
+ <FeatureUtf8String>true</FeatureUtf8String>
<!-- FeatureXXX>true</FeatureXXX -->
</PropertyGroup>
diff --git a/clrdefinitions.cmake b/clrdefinitions.cmake
index df6c0f39db..1a8089f243 100644
--- a/clrdefinitions.cmake
+++ b/clrdefinitions.cmake
@@ -6,6 +6,7 @@ set(PRERELEASE 0)
# Features we're currently flighting, but don't intend to ship in officially supported releases
if (PRERELEASE)
+ add_definitions(-DFEATURE_UTF8STRING=1)
# add_definitions(-DFEATURE_XXX=1)
endif (PRERELEASE)
diff --git a/configurecompiler.cmake b/configurecompiler.cmake
index 39c2e7e410..c893e44282 100644
--- a/configurecompiler.cmake
+++ b/configurecompiler.cmake
@@ -485,6 +485,7 @@ if (CLR_CMAKE_PLATFORM_UNIX)
add_compile_options(-Wno-unused-variable)
add_compile_options(-Wno-unused-but-set-variable)
add_compile_options(-fms-extensions)
+ add_compile_options(-Wno-unknown-pragmas)
endif()
# Some architectures (e.g., ARM) assume char type is unsigned while CoreCLR assumes char is signed
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 2fa349ada3..e364fcf020 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -3,13 +3,13 @@
<ProductDependencies>
</ProductDependencies>
<ToolsetDependencies>
- <Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="1.0.0-beta.19162.7">
+ <Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="1.0.0-beta.19170.2">
<Uri>https://github.com/dotnet/arcade</Uri>
- <Sha>443dea11f8649fe12fedf60cfab0a4b2b20bd153</Sha>
+ <Sha>f70fa34786cff993625a4548ae0125335eabe82e</Sha>
</Dependency>
- <Dependency Name="Microsoft.DotNet.Helix.Sdk" Version="2.0.0-beta.19162.7">
+ <Dependency Name="Microsoft.DotNet.Helix.Sdk" Version="2.0.0-beta.19170.2">
<Uri>https://github.com/dotnet/arcade</Uri>
- <Sha>443dea11f8649fe12fedf60cfab0a4b2b20bd153</Sha>
+ <Sha>f70fa34786cff993625a4548ae0125335eabe82e</Sha>
</Dependency>
<Dependency Name="Microsoft.Private.CoreFx.NETCoreApp" Version="4.6.0-preview4.19164.7">
<Uri>https://github.com/dotnet/corefx</Uri>
@@ -19,9 +19,9 @@
<Uri>https://github.com/dotnet/corefx</Uri>
<Sha>d6a30736858f91b297fdd3ed4e3d1dfde67bdbdb</Sha>
</Dependency>
- <Dependency Name="Microsoft.NETCore.App" Version="3.0.0-preview4-27516-06">
+ <Dependency Name="Microsoft.NETCore.App" Version="3.0.0-preview4-27521-01">
<Uri>https://github.com/dotnet/core-setup</Uri>
- <Sha>66294ec25258cc1bd566d772d3bb3bfaa550c05e</Sha>
+ <Sha>18780678da576d8c629066f50af30159a8859b2f</Sha>
</Dependency>
<Dependency Name="optimization.IBC.CoreCLR" Version="99.99.99-master-20190313.3">
<Uri>https://dev.azure.com/dnceng/internal/_git/dotnet-optimization</Uri>
diff --git a/eng/Versions.props b/eng/Versions.props
index 8f693a1059..bbf67e01d0 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -10,7 +10,7 @@
<MicrosoftNetFrameworkReferenceAssembliesVersion>1.0.0-alpha-004</MicrosoftNetFrameworkReferenceAssembliesVersion>
<MicrosoftPrivateCoreFxNETCoreAppVersion>4.6.0-preview4.19164.7</MicrosoftPrivateCoreFxNETCoreAppVersion>
<MicrosoftNETCorePlatformsVersion>3.0.0-preview4.19164.7</MicrosoftNETCorePlatformsVersion>
- <MicrosoftNETCoreAppVersion>3.0.0-preview4-27516-06</MicrosoftNETCoreAppVersion>
+ <MicrosoftNETCoreAppVersion>3.0.0-preview4-27521-01</MicrosoftNETCoreAppVersion>
<optimizationIBCCoreCLRVersion>99.99.99-master-20190313.3</optimizationIBCCoreCLRVersion>
<optimizationPGOCoreCLRVersion>99.99.99-master-20190313.3</optimizationPGOCoreCLRVersion>
</PropertyGroup>
diff --git a/eng/build-job.yml b/eng/build-job.yml
index 7e9f631c01..1d3cde4baf 100644
--- a/eng/build-job.yml
+++ b/eng/build-job.yml
@@ -195,10 +195,3 @@ jobs:
targetPath: $(Build.SourcesDirectory)/bin/Logs
continueOnError: true
condition: always()
-
- # Kill tasks that hold onto files on Windows. Otherwise git clean
- # may fail for later jobs on the same agent.
- - ${{ if eq(parameters.osGroup, 'Windows_NT') }}:
- - script: eng/kill_tasks.cmd
- displayName: Kill tasks that hold on to files
- condition: always()
diff --git a/eng/common/PublishToPackageFeed.proj b/eng/common/PublishToPackageFeed.proj
index b26d28a90b..25362ff060 100644
--- a/eng/common/PublishToPackageFeed.proj
+++ b/eng/common/PublishToPackageFeed.proj
@@ -11,11 +11,17 @@
</PropertyGroup>
<Import Project="$(MSBuildThisFileDirectory)DefaultVersions.props" Condition="Exists('$(MSBuildThisFileDirectory)DefaultVersions.props')" />
+
+ <!--
+ This won't be necessary once we solve this issue:
+ https://github.com/dotnet/arcade/issues/2266
+ -->
+ <Import Project="$(MSBuildThisFileDirectory)ArtifactsCategory.props" Condition="Exists('$(MSBuildThisFileDirectory)ArtifactsCategory.props')" />
<Import Project="$(NuGetPackageRoot)microsoft.dotnet.build.tasks.feed\$(MicrosoftDotNetBuildTasksFeedVersion)\build\Microsoft.DotNet.Build.Tasks.Feed.targets" />
<Target Name="PublishToFeed">
- <Error Condition="'$(TargetStaticFeed)' == ''" Text="TargetStaticFeed: Target feed for publishing assets wasn't provided." />
+ <Error Condition="'$(ArtifactsCategory)' == ''" Text="ArtifactsCategory: The artifacts' category produced by the build wasn't provided." />
<Error Condition="'$(AccountKeyToStaticFeed)' == ''" Text="AccountKeyToStaticFeed: Account key for target feed wasn't provided." />
<Error Condition="'$(ManifestsBasePath)' == ''" Text="Full path to asset manifests directory wasn't provided." />
<Error Condition="'$(BlobBasePath)' == '' AND '$(PackageBasePath)' == ''" Text="A valid full path to BlobBasePath of PackageBasePath is required." />
@@ -26,7 +32,21 @@
</ItemGroup>
<Error Condition="'@(ManifestFiles)' == ''" Text="No manifest file was found in the provided path: $(ManifestsBasePath)" />
-
+
+ <!--
+ For now the type of packages being published will be informed for the whole build.
+ Eventually this will be specified on a per package basis:
+ TODO: https://github.com/dotnet/arcade/issues/2266
+ -->
+ <PropertyGroup>
+ <TargetStaticFeed Condition="'$(ArtifactsCategory.ToUpper())' == '.NETCORE'">https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json</TargetStaticFeed>
+ <TargetStaticFeed Condition="'$(ArtifactsCategory.ToUpper())' == '.NETCOREVALIDATION'">https://dotnetfeed.blob.core.windows.net/arcade-validation/index.json</TargetStaticFeed>
+ </PropertyGroup>
+
+ <Error
+ Condition="'$(TargetStaticFeed)' == ''"
+ Text="'$(ArtifactsCategory)' wasn't recognized as a valid artifact category. Valid categories are: '.NetCore' and '.NetCoreValidation'" />
+
<!-- Iterate publishing assets from each manifest file. -->
<PushArtifactsInManifestToFeed
ExpectedFeedUrl="$(TargetStaticFeed)"
diff --git a/eng/common/generate-graph-files.ps1 b/eng/common/generate-graph-files.ps1
index e09c64e9f6..76f57076a3 100644
--- a/eng/common/generate-graph-files.ps1
+++ b/eng/common/generate-graph-files.ps1
@@ -3,7 +3,8 @@ Param(
[Parameter(Mandatory=$true)][string] $gitHubPat, # GitHub personal access token from https://github.com/settings/tokens (no auth scopes needed)
[Parameter(Mandatory=$true)][string] $azdoPat, # Azure Dev Ops tokens from https://dev.azure.com/dnceng/_details/security/tokens (code read scope needed)
[Parameter(Mandatory=$true)][string] $outputFolder, # Where the graphviz.txt file will be created
- [string] $darcVersion = '1.1.0-beta.19156.4', # darc's version
+ [string] $darcVersion = '1.1.0-beta.19169.5', # darc's version
+ [string] $graphvizVersion = '2.38', # GraphViz version
[switch] $includeToolset # Whether the graph should include toolset dependencies or not. i.e. arcade, optimization. For more about
# toolset dependencies see https://github.com/dotnet/arcade/blob/master/Documentation/Darc.md#toolset-vs-product-dependencies
)
@@ -11,6 +12,8 @@ Param(
$ErrorActionPreference = "Stop"
. $PSScriptRoot\tools.ps1
+Import-Module -Name (Join-Path $PSScriptRoot "native\CommonLibrary.psm1")
+
function CheckExitCode ([string]$stage)
{
$exitCode = $LASTEXITCODE
@@ -27,27 +30,48 @@ try {
. .\darc-init.ps1 -darcVersion $darcVersion
CheckExitCode "Running darc-init"
+ $engCommonBaseDir = Join-Path $PSScriptRoot "native\"
+ $graphvizInstallDir = CommonLibrary\Get-NativeInstallDirectory
+ $nativeToolBaseUri = "https://netcorenativeassets.blob.core.windows.net/resource-packages/external"
+ $installBin = Join-Path $graphvizInstallDir "bin"
+
+ Write-Host "Installing dot..."
+ .\native\install-tool.ps1 -ToolName graphviz -InstallPath $installBin -BaseUri $nativeToolBaseUri -CommonLibraryDirectory $engCommonBaseDir -Version $graphvizVersion -Verbose
+
$darcExe = "$env:USERPROFILE\.dotnet\tools"
$darcExe = Resolve-Path "$darcExe\darc.exe"
Create-Directory $outputFolder
+ # Generate 3 graph descriptions:
+ # 1. Flat with coherency information
+ # 2. Graphviz (dot) file
+ # 3. Standard dependency graph
$graphVizFilePath = "$outputFolder\graphviz.txt"
- $graphFilePath = "$outputFolder\graph.txt"
- $options = "get-dependency-graph --graphviz '$graphVizFilePath' --github-pat $gitHubPat --azdev-pat $azdoPat --password $barToken --output-file $graphFilePath"
+ $graphVizImageFilePath = "$outputFolder\graph.png"
+ $normalGraphFilePath = "$outputFolder\graph-full.txt"
+ $flatGraphFilePath = "$outputFolder\graph-flat.txt"
+ $baseOptions = "get-dependency-graph --github-pat $gitHubPat --azdev-pat $azdoPat --password $barToken"
if ($includeToolset) {
Write-Host "Toolsets will be included in the graph..."
- $options += " --include-toolset"
+ $baseOptions += " --include-toolset"
}
- Write-Host "Generating dependency graph..."
- Invoke-Expression "& `"$darcExe`" $options"
- CheckExitCode "Generating dependency graph"
+ Write-Host "Generating standard dependency graph..."
+ Invoke-Expression "& `"$darcExe`" $baseOptions --output-file $normalGraphFilePath"
+ CheckExitCode "Generating normal dependency graph"
+
+ Write-Host "Generating flat dependency graph and graphviz file..."
+ Invoke-Expression "& `"$darcExe`" $baseOptions --flat --coherency --graphviz $graphVizFilePath --output-file $flatGraphFilePath"
+ CheckExitCode "Generating flat and graphviz dependency graph"
+
+ Write-Host "Generating graph image $graphVizFilePath"
+ $dotFilePath = Join-Path $installBin "graphviz\$graphvizVersion\release\bin\dot.exe"
+ Invoke-Expression "& `"$dotFilePath`" -Tpng -o'$graphVizImageFilePath' `"$graphVizFilePath`""
+ CheckExitCode "Generating graphviz image"
- $graph = Get-Content $graphVizFilePath
- Set-Content $graphVizFilePath -Value "Paste the following digraph object in http://www.webgraphviz.com `r`n", $graph
- Write-Host "'$graphVizFilePath' and '$graphFilePath' created!"
+ Write-Host "'$graphVizFilePath', '$flatGraphFilePath', '$normalGraphFilePath' and '$graphVizImageFilePath' created!"
}
catch {
if (!$includeToolset) {
@@ -58,4 +82,6 @@ catch {
Write-Host $_.Exception
Write-Host $_.ScriptStackTrace
ExitWithExitCode 1
+} finally {
+ Pop-Location
} \ No newline at end of file
diff --git a/eng/common/init-tools-native.ps1 b/eng/common/init-tools-native.ps1
index e25c60fed4..495a563a75 100644
--- a/eng/common/init-tools-native.ps1
+++ b/eng/common/init-tools-native.ps1
@@ -98,10 +98,20 @@ try {
Write-Verbose "Executing '$LocalInstallerCommand'"
Invoke-Expression "$LocalInstallerCommand"
if ($LASTEXITCODE -Ne "0") {
- Write-Error "Execution failed"
- exit 1
+ $errMsg = "$ToolName installation failed"
+ if ((Get-Variable 'DoNotAbortNativeToolsInstallationOnFailure' -ErrorAction 'SilentlyContinue') -and $DoNotAbortNativeToolsInstallationOnFailure) {
+ Write-Warning $errMsg
+ $toolInstallationFailure = $true
+ } else {
+ Write-Error $errMsg
+ exit 1
+ }
}
}
+
+ if ((Get-Variable 'toolInstallationFailure' -ErrorAction 'SilentlyContinue') -and $toolInstallationFailure) {
+ exit 1
+ }
}
else {
Write-Host "No native tools defined in global.json"
diff --git a/eng/common/templates/job/publish-build-assets.yml b/eng/common/templates/job/publish-build-assets.yml
index d6d8697cbd..620bd3c62e 100644
--- a/eng/common/templates/job/publish-build-assets.yml
+++ b/eng/common/templates/job/publish-build-assets.yml
@@ -20,6 +20,9 @@ parameters:
# if 'true', the build won't run any of the internal only steps, even if it is running in non-public projects.
runAsPublic: false
+ # Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing
+ publishUsingPipelines: false
+
jobs:
- job: Asset_Registry_Publish
@@ -52,6 +55,7 @@ jobs:
/p:ManifestsPath='$(Build.StagingDirectory)/Download/AssetManifests'
/p:BuildAssetRegistryToken=$(MaestroAccessToken)
/p:MaestroApiEndpoint=https://maestro-prod.westus2.cloudapp.azure.com
+ /p:PublishUsingPipelines=${{ parameters.publishUsingPipelines }}
/p:Configuration=$(_BuildConfig)
condition: ${{ parameters.condition }}
continueOnError: ${{ parameters.continueOnError }}
diff --git a/eng/common/templates/jobs/jobs.yml b/eng/common/templates/jobs/jobs.yml
index 06ed58de41..6a2f98c036 100644
--- a/eng/common/templates/jobs/jobs.yml
+++ b/eng/common/templates/jobs/jobs.yml
@@ -13,6 +13,9 @@ parameters:
# Optional: Enable publishing to the build asset registry
enablePublishBuildAssets: false
+
+ # Optional: Enable publishing using release pipelines
+ enablePublishUsingPipelines: false
graphFileGeneration:
# Optional: Enable generating the graph files at the end of the build
@@ -73,6 +76,7 @@ jobs:
pool:
vmImage: vs2017-win2016
runAsPublic: ${{ parameters.runAsPublic }}
+ publishUsingPipelines: ${{ parameters.enablePublishUsingPipelines }}
enablePublishBuildArtifacts: ${{ parameters.enablePublishBuildArtifacts }}
- ${{ if and(eq(parameters.graphFileGeneration.enabled, true), eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
diff --git a/eng/common/templates/steps/send-to-helix.yml b/eng/common/templates/steps/send-to-helix.yml
index 0925e8ebd1..0187597681 100644
--- a/eng/common/templates/steps/send-to-helix.yml
+++ b/eng/common/templates/steps/send-to-helix.yml
@@ -2,7 +2,7 @@ parameters:
HelixSource: 'pr/default' # required -- sources must start with pr/, official/, prodcon/, or agent/
HelixType: 'tests/default/' # required -- Helix telemetry which identifies what type of data this is; should include "test" for clarity and must end in '/'
HelixBuild: $(Build.BuildNumber) # required -- the build number Helix will use to identify this -- automatically set to the AzDO build number
- HelixTargetQueues: '' # required -- semicolon delimited list of Helix queues to test on; see https://helix.dot.net/api/2018-03-14/info/queues for a list of queues
+ HelixTargetQueues: '' # required -- semicolon delimited list of Helix queues to test on; see https://helix.dot.net/ for a list of queues
HelixAccessToken: '' # required -- access token to make Helix API requests; should be provided by the appropriate variable group
HelixPreCommands: '' # optional -- commands to run before Helix work item execution
HelixPostCommands: '' # optional -- commands to run after Helix work item execution
@@ -11,6 +11,7 @@ parameters:
WorkItemTimeout: '' # optional -- a timeout in seconds for the work item command; requires WorkItemDirectory; incompatible with XUnitProjects
CorrelationPayloadDirectory: '' # optional -- a directory to zip up and send to Helix as a correlation payload
XUnitProjects: '' # optional -- semicolon delimited list of XUnitProjects to parse and send to Helix; requires XUnitRuntimeTargetFramework, XUnitPublishTargetFramework, XUnitRunnerVersion, and IncludeDotNetCli=true
+ XUnitWorkItemTimeout: '' # optional -- the workitem timeout in seconds for all workitems created from the xUnit projects specified by XUnitProjects
XUnitPublishTargetFramework: '' # optional -- framework to use to publish your xUnit projects
XUnitRuntimeTargetFramework: '' # optional -- framework to use for the xUnit console runner
XUnitRunnerVersion: '' # optional -- version of the xUnit nuget package you wish to use on Helix; required for XUnitProjects
@@ -42,6 +43,7 @@ steps:
WorkItemTimeout: ${{ parameters.WorkItemTimeout }}
CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }}
XUnitProjects: ${{ parameters.XUnitProjects }}
+ XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }}
XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }}
XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }}
XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }}
@@ -70,6 +72,7 @@ steps:
WorkItemTimeout: ${{ parameters.WorkItemTimeout }}
CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }}
XUnitProjects: ${{ parameters.XUnitProjects }}
+ XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }}
XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }}
XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }}
XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }}
diff --git a/eng/kill_tasks.cmd b/eng/kill_tasks.cmd
deleted file mode 100644
index ee7099c94d..0000000000
--- a/eng/kill_tasks.cmd
+++ /dev/null
@@ -1,10 +0,0 @@
-@if not defined _echo @echo off
-setlocal EnableDelayedExpansion
-
-:: Check if VBCSCompiler.exe is running
-tasklist /fi "imagename eq VBCSCompiler.exe" |find ":" > nul
-:: Compiler is running if errorlevel == 1
-if errorlevel 1 (
- echo Stop VBCSCompiler.exe execution.
- for /f "tokens=2 delims=," %%F in ('tasklist /nh /fi "imagename eq VBCSCompiler.exe" /fo csv') do taskkill /f /PID %%~F
-)
diff --git a/eng/platform-matrix.yml b/eng/platform-matrix.yml
index 69d4cbe15f..056b3632f7 100644
--- a/eng/platform-matrix.yml
+++ b/eng/platform-matrix.yml
@@ -33,9 +33,9 @@ jobs:
containerName: ubuntu_1404_arm_cross_build_image
helixQueues:
${{ if eq(variables['System.TeamProject'], 'public') }}:
- # TODO: add Ubuntu.1404.Arm32.Open once Jenkins has been shutdown
- asString: ''
- asArray: []
+ asString: 'Ubuntu.1404.Arm32.Open'
+ asArray:
+ - Ubuntu.1404.Arm32.Open
${{ if eq(variables['System.TeamProject'], 'internal') }}:
# We don't have any Linux/arm32 internal Helix queues
asString: ''
@@ -55,21 +55,21 @@ jobs:
containerName: ubuntu_1604_arm64_cross_build_image
helixQueues:
${{ if and(eq(variables['System.TeamProject'], 'public'), in(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI')) }}:
- asString: 'Ubuntu.1804.Arm64.Open'
+ asString: '(Ubuntu.18.04.Arm64.Open)Ubuntu.1604.Arm64.Docker.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-18.04-helix-arm64v8-0a0ebdd-20190312220351'
asArray:
- - Ubuntu.1804.Arm64.Open
+ - (Ubuntu.18.04.Arm64.Open)Ubuntu.1604.Arm64.Docker.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-18.04-helix-arm64v8-0a0ebdd-20190312220351
${{ if and(eq(variables['System.TeamProject'], 'public'), notIn(variables['Build.Reason'], 'PullRequest', 'IndividualCI', 'BatchedCI')) }}:
# TODO: add Ubuntu.1604.Arm64.Open once Jenkins has been shutdown
- asString: 'Debian.9.Arm64.Open,Ubuntu.1804.Arm64.Open'
+ asString: '(Debian.9.Arm64 on Docker)Ubuntu.1604.Arm64.Docker@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-9-helix-arm64v8-0a0ebdd-20190312215438.Open on Docker)Ubuntu.1604.Arm64.Docker.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-9-helix-arm64v8-0a0ebdd-20190312215438,(Ubuntu.18.04.Arm64.Open)Ubuntu.1604.Arm64.Docker.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-18.04-helix-arm64v8-0a0ebdd-20190312220351'
asArray:
- - Debian.9.Arm64.Open
- - Ubuntu.1804.Arm64.Open
+ - (Debian.9.Arm64 on Docker)Ubuntu.1604.Arm64.Docker@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-9-helix-arm64v8-0a0ebdd-20190312215438.Open on Docker)Ubuntu.1604.Arm64.Docker.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-9-helix-arm64v8-0a0ebdd-20190312215438
+ - (Ubuntu.18.04.Arm64.Open)Ubuntu.1604.Arm64.Docker.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-18.04-helix-arm64v8-0a0ebdd-20190312220351
${{ if eq(variables['System.TeamProject'], 'internal') }}:
- asString: 'Debian.9.Arm64,Ubuntu.1604.Arm64,Ubuntu.1804.Arm64'
+ asString: '(Debian.9.Arm64 on Docker)Ubuntu.1604.Arm64.Docker@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-9-helix-arm64v8-0a0ebdd-20190312215438,Ubuntu.1604.Arm64,(Ubuntu.18.04.Arm64)Ubuntu.1604.Arm64.Docker@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-18.04-helix-arm64v8-0a0ebdd-20190312220351'
asArray:
- - Debian.9.Arm64
+ - (Debian.9.Arm64 on Docker)Ubuntu.1604.Arm64.Docker@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-9-helix-arm64v8-0a0ebdd-20190312215438
- Ubuntu.1604.Arm64
- - Ubuntu.1804.Arm64
+ - (Ubuntu.18.04.Arm64)Ubuntu.1604.Arm64.Docker@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-18.04-helix-arm64v8-0a0ebdd-20190312220351
crossrootfsDir: '/crossrootfs/arm64'
${{ insert }}: ${{ parameters.jobParameters }}
diff --git a/global.json b/global.json
index 87688f7989..1fd361f19c 100644
--- a/global.json
+++ b/global.json
@@ -7,7 +7,7 @@
"python": "2.7.15"
},
"msbuild-sdks": {
- "Microsoft.DotNet.Arcade.Sdk": "1.0.0-beta.19162.7",
- "Microsoft.DotNet.Helix.Sdk": "2.0.0-beta.19162.7"
+ "Microsoft.DotNet.Arcade.Sdk": "1.0.0-beta.19170.2",
+ "Microsoft.DotNet.Helix.Sdk": "2.0.0-beta.19170.2"
}
}
diff --git a/netci.groovy b/netci.groovy
index 6ab04375fb..2b4355ca01 100755
--- a/netci.groovy
+++ b/netci.groovy
@@ -169,58 +169,17 @@ class Constants {
// Valid PR trigger combinations.
def static prTriggeredValidInnerLoopCombos = [
'Windows_NT': [
- 'x64': [
- 'Checked'
- ],
- 'x86': [
- 'Checked',
- 'Release'
- ],
'arm': [
- 'Debug',
'Checked'
],
'arm64': [
- 'Debug',
'Checked'
]
],
'Windows_NT_BuildOnly': [
- 'x64': [
- 'Checked',
- 'Release'
- ],
- 'x86': [
- 'Checked',
- 'Release'
- ],
'arm': [
'Checked'
],
- ],
- 'Ubuntu': [
- 'x64': [
- 'Checked'
- ],
- 'arm': [
- 'Checked'
- ]
- ],
- 'Ubuntu16.04': [
- 'arm64': [
- 'Checked'
- ]
- ],
- 'CentOS7.1': [
- 'x64': [
- 'Debug',
- 'Checked'
- ]
- ],
- 'OSX10.12': [
- 'x64': [
- 'Checked'
- ]
]
]
@@ -679,8 +638,9 @@ def static addPeriodicTriggerHelper(def job, String cronString, boolean alwaysRu
}
def static addGithubPushTriggerHelper(def job) {
- addToMergeView(job)
- Utilities.addGithubPushTrigger(job)
+ // Disable all Push trigger jobs. All jobs will need to be requested.
+ // addToMergeView(job)
+ // Utilities.addGithubPushTrigger(job)
}
@@ -2522,14 +2482,14 @@ def static calculateBuildCommands(def newJob, def scenario, def branch, def isPR
// TODO: Add -target_branch and -commit_hash arguments based on GitHub variables.
buildCommands += "python -u \${WORKSPACE}/tests/scripts/run-pmi-diffs.py -arch ${architecture} -ci_arch ${architecture} -build_type ${configuration} --skip_diffs"
- // ZIP what we created.
- buildCommands += "zip -r product.${os}.${architecture}.${lowerConfiguration}.zip ./bin/Product/Linux.${architecture}.${configuration}"
- buildCommands += "zip -r product.baseline.${os}.${architecture}.${lowerConfiguration}.zip ./_/pmi/base/bin/Product/Linux.${architecture}.${configuration}"
- buildCommands += "zip -r coreroot.${os}.${architecture}.${lowerConfiguration}.zip ./bin/tests/Linux.${architecture}.${configuration}/Tests/Core_Root"
- buildCommands += "zip -r coreroot.baseline.${os}.${architecture}.${lowerConfiguration}.zip ./_/pmi/base/bin/tests/Linux.${architecture}.${configuration}/Tests/Core_Root"
+ // Archive what we created.
+ buildCommands += "tar -czf product.${os}.${architecture}.${lowerConfiguration}.tgz ./bin/Product/Linux.${architecture}.${configuration}"
+ buildCommands += "tar -czf product.baseline.${os}.${architecture}.${lowerConfiguration}.tgz ./_/pmi/base/bin/Product/Linux.${architecture}.${configuration}"
+ buildCommands += "tar -czf coreroot.${os}.${architecture}.${lowerConfiguration}.tgz ./bin/tests/Linux.${architecture}.${configuration}/Tests/Core_Root"
+ buildCommands += "tar -czf coreroot.baseline.${os}.${architecture}.${lowerConfiguration}.tgz ./_/pmi/base/bin/tests/Linux.${architecture}.${configuration}/Tests/Core_Root"
// Archive the built artifacts
- Utilities.addArchival(newJob, "product.${os}.${architecture}.${lowerConfiguration}.zip,product.baseline.${os}.${architecture}.${lowerConfiguration}.zip,coreroot.${os}.${architecture}.${lowerConfiguration}.zip,coreroot.baseline.${os}.${architecture}.${lowerConfiguration}.zip")
+ Utilities.addArchival(newJob, "product.${os}.${architecture}.${lowerConfiguration}.tgz,product.baseline.${os}.${architecture}.${lowerConfiguration}.tgz,coreroot.${os}.${architecture}.${lowerConfiguration}.tgz,coreroot.baseline.${os}.${architecture}.${lowerConfiguration}.tgz")
}
else {
// Then, using the same docker image, build the tests and generate the CORE_ROOT layout.
@@ -2581,7 +2541,17 @@ def static calculateBuildCommands(def newJob, def scenario, def branch, def isPR
// Returns true if the job should be generated.
def static shouldGenerateJob(def scenario, def isPR, def architecture, def configuration, def os, def isBuildOnly)
{
- // The various "innerloop" jobs are only available as PR triggered.
+ // Innerloop jobs (except corefx_innerloop) are no longer created in Jenkins
+ if (isInnerloopTestScenario(scenario)) {
+ assert scenario != 'corefx_innerloop'
+ return false;
+ }
+
+ if (!isPR) {
+ if (scenario == 'corefx_innerloop') {
+ return false
+ }
+ }
if (!isPR) {
if (isInnerloopTestScenario(scenario)) {
@@ -3290,10 +3260,10 @@ def static CreateOtherTestJob(def dslFactory, def project, def branch, def archi
if (isPmiAsmDiffsScenario) {
def workspaceRelativeRootLinux = "_/pmi"
shell("mkdir -p ${workspaceRelativeRootLinux}")
- shell("wget --progress=dot:giga ${inputUrlRoot}/product.${os}.${architecture}.${lowerConfiguration}.zip")
- shell("wget --progress=dot:giga ${inputUrlRoot}/product.baseline.${os}.${architecture}.${lowerConfiguration}.zip")
- shell("wget --progress=dot:giga ${inputUrlRoot}/coreroot.${os}.${architecture}.${lowerConfiguration}.zip")
- shell("wget --progress=dot:giga ${inputUrlRoot}/coreroot.baseline.${os}.${architecture}.${lowerConfiguration}.zip")
+ shell("wget --progress=dot:giga ${inputUrlRoot}/product.${os}.${architecture}.${lowerConfiguration}.tgz")
+ shell("wget --progress=dot:giga ${inputUrlRoot}/product.baseline.${os}.${architecture}.${lowerConfiguration}.tgz")
+ shell("wget --progress=dot:giga ${inputUrlRoot}/coreroot.${os}.${architecture}.${lowerConfiguration}.tgz")
+ shell("wget --progress=dot:giga ${inputUrlRoot}/coreroot.baseline.${os}.${architecture}.${lowerConfiguration}.tgz")
}
else if (doCoreFxTesting) {
shell("mkdir -p ${workspaceRelativeFxRootLinux}")
@@ -3325,11 +3295,10 @@ def static CreateOtherTestJob(def dslFactory, def project, def branch, def archi
}
if (isPmiAsmDiffsScenario) {
- // TODO: add back "-q" when we know it works
- shell("unzip -o ./product.${os}.${architecture}.${lowerConfiguration}.zip || exit 0")
- shell("unzip -o ./product.baseline.${os}.${architecture}.${lowerConfiguration}.zip || exit 0")
- shell("unzip -o ./coreroot.${os}.${architecture}.${lowerConfiguration}.zip || exit 0")
- shell("unzip -o ./coreroot.baseline.${os}.${architecture}.${lowerConfiguration}.zip || exit 0")
+ shell("tar -xzf ./product.${os}.${architecture}.${lowerConfiguration}.tgz || exit 0")
+ shell("tar -xzf ./product.baseline.${os}.${architecture}.${lowerConfiguration}.tgz || exit 0")
+ shell("tar -xzf ./coreroot.${os}.${architecture}.${lowerConfiguration}.tgz || exit 0")
+ shell("tar -xzf ./coreroot.baseline.${os}.${architecture}.${lowerConfiguration}.tgz || exit 0")
}
// CoreFX testing downloads the CoreFX tests, not the coreclr tests. Also, unzip the built CoreFX layout/runtime directories.
else if (doCoreFxTesting) {
@@ -3376,7 +3345,7 @@ def static CreateOtherTestJob(def dslFactory, def project, def branch, def archi
shell("""\
python -u \${WORKSPACE}/tests/scripts/run-pmi-diffs.py -arch ${architecture} -ci_arch ${architecture} -build_type ${configuration} --skip_baseline_build""")
- shell("zip -r dasm.${os}.${architecture}.${configuration}.zip ./_/pmi/asm")
+ shell("tar -czf dasm.${os}.${architecture}.${configuration}.tgz ./_/pmi/asm")
}
else if (doCoreFxTesting) {
shell("""\
@@ -3418,7 +3387,7 @@ ${runScript} \\
if (isPmiAsmDiffsScenario) {
// Archive the asm
- Utilities.addArchival(newJob, "dasm.${os}.${architecture}.${configuration}.zip")
+ Utilities.addArchival(newJob, "dasm.${os}.${architecture}.${configuration}.tgz")
}
else if (doCoreFxTesting) {
Utilities.addArchival(newJob, "${workspaceRelativeFxRootLinux}/artifacts/bin/**/testResults.xml", "", /* doNotFailIfNothingArchived */ true, /* archiveOnlyIfSuccessful */ false)
@@ -3572,6 +3541,15 @@ def static shouldGenerateFlowJob(def scenario, def isPR, def architecture, def c
}
}
+ // Disable flow jobs for innerloop pr.
+ //
+ // The only exception is windows arm(64)
+ if (isInnerloopTestScenario(scenario) && isPR && os != 'Windows_NT') {
+ assert scenario != 'corefx_innerloop'
+
+ return false;
+ }
+
// Filter based on OS and architecture.
switch (architecture) {
diff --git a/perf.groovy b/perf.groovy
index ac7222d175..a8316e4731 100644
--- a/perf.groovy
+++ b/perf.groovy
@@ -41,7 +41,7 @@ def static getOSGroup(def os) {
def newJob = job(Utilities.getFullJobName(project, jobName, isPR)) {
// Set the label.
if (isSmoketest) {
- label('Windows.Amd64.ClientRS4.DevEx.15.8.Perf')
+ label('Windows.10.Amd64.ClientRS4.DevEx.15.8.Open')
python = "C:\\python3.7.0\\python.exe"
}
else {
@@ -767,7 +767,7 @@ def static getFullThroughputJobName(def project, def os, def arch, def isPR) {
['x64', 'x86'].each { arch ->
def architecture = arch
def newJob = job(Utilities.getFullJobName(project, "sizeondisk_${arch}", false)) {
- label('Windows.Amd64.ClientRS4.DevEx.15.8.Perf')
+ label('Windows.10.Amd64.ClientRS4.DevEx.15.8.Open')
wrappers {
credentialsBinding {
@@ -853,7 +853,7 @@ def static getFullThroughputJobName(def project, def os, def arch, def isPR) {
['full_opt'].each { opt_level ->
def architecture = arch
def newJob = job(Utilities.getFullJobName(project, "perf_illink_${os}_${arch}_${opt_level}_${jit}", isPR)) {
- label('Windows.Amd64.ClientRS4.DevEx.15.8.Perf')
+ label('Windows.10.Amd64.ClientRS4.DevEx.15.8.Open')
def testEnv = ""
def python = "C:\\python3.7.0\\python.exe"
diff --git a/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/System.Private.CoreLib.csproj
index 6e73aeffa5..e75a0fc2e4 100644
--- a/src/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -112,9 +112,14 @@
<!-- CLR Features -->
<Import Project="$(MSBuildThisFileDirectory)..\..\clr.coreclr.props" />
<Import Project="$(MSBuildThisFileDirectory)..\..\clr.defines.targets" />
+ <!-- Experimental features -->
+ <PropertyGroup Condition="'$(FeatureUtf8String)' == 'true'">
+ <DefineConstants>$(DefineConstants);FEATURE_UTF8STRING</DefineConstants>
+ </PropertyGroup>
<!-- Sources -->
<ItemGroup>
<Compile Include="$(BclSourcesRoot)\Internal\Console.cs" />
+ <Compile Include="$(BclSourcesRoot)\Internal\Runtime\InteropServices\IsolatedComponentLoadContext.cs" />
<Compile Include="$(BclSourcesRoot)\Microsoft\Win32\UnsafeNativeMethods.cs" />
<Compile Include="$(BclSourcesRoot)\System\__Canon.cs" />
<Compile Include="$(BclSourcesRoot)\System\AppContext.CoreCLR.cs" />
@@ -154,7 +159,7 @@
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\SymbolStore\Token.cs" />
<Compile Include="$(BclSourcesRoot)\System\Enum.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Environment.CoreCLR.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Exception.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Exception.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\GC.cs" />
<Compile Include="$(BclSourcesRoot)\System\Globalization\GlobalizationMode.cs" />
<Compile Include="$(BclSourcesRoot)\System\Internal.cs" />
@@ -274,6 +279,14 @@
<Compile Include="shared\Interop\Windows\Ole32\Interop.CoTaskMemAlloc.cs" />
<Compile Include="shared\Interop\Windows\OleAut32\Interop.SysAllocStringByteLen.cs" />
</ItemGroup>
+ <ItemGroup Condition="'$(FeatureUtf8String)' == 'true'">
+ <Compile Include="$(BclSourcesRoot)\System\Char8.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Utf8Extensions.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Utf8String.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Utf8String.Construction.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Utf8String.Manipulation.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Utf8String.Searching.cs" />
+ </ItemGroup>
<ItemGroup>
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\Eventing\XplatEventLogger.cs" Condition="'$(FeatureXplatEventSource)' == 'true'" />
<Compile Include="$(IntermediateOutputPath)..\Eventing\NativeRuntimeEventSource.cs" Condition="'$(FeaturePerfTracing)' == 'true'"/>
@@ -353,6 +366,7 @@
<Compile Include="$(BclSourcesRoot)\System\Threading\ClrThreadPoolBoundHandle.Unix.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsWindows)' == 'true'">
+ <Compile Include="$(BclSourcesRoot)\Internal\Runtime\InteropServices\InMemoryAssemblyLoader.cs" />
<Compile Include="$(BclSourcesRoot)\System\DateTime.Windows.cs" />
<Compile Include="$(BclSourcesRoot)\Interop\Windows\OleAut32\Interop.VariantClear.cs" />
<Compile Include="$(BclSourcesRoot)\System\ApplicationModel.Windows.cs" />
diff --git a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
index 85ba8b8cd4..3cd36e0f27 100644
--- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
+++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
@@ -226,6 +226,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Enum.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\EventArgs.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\EventHandler.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Exception.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\ExecutionEngineException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\FieldAccessException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\FlagsAttribute.cs" />
@@ -360,6 +361,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\MemoryDebugView.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MemoryExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MemoryExtensions.Fast.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\MemoryExtensions.Trim.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MethodAccessException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MidpointRounding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\MissingFieldException.cs" />
@@ -801,6 +803,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Text\UTF8Encoding.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Text\ValueStringBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Text\Unicode\Utf8.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Text\Unicode\Utf8Utility.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\TimeSpan.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\ThreadAttributes.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\AbandonedMutexException.cs" />
@@ -1049,6 +1052,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Internal\IO\File.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeFileHandle.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeFindHandle.Windows.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffer.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\DebugProvider.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Globalization\CalendarData.Windows.cs" />
@@ -1201,6 +1205,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Interop\Unix\System.Native\Interop.Write.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Internal\IO\File.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeFileHandle.Unix.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Buffer.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\DateTime.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Diagnostics\DebugProvider.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Environment.Unix.cs" />
diff --git a/src/System.Private.CoreLib/shared/System/Buffer.Unix.cs b/src/System.Private.CoreLib/shared/System/Buffer.Unix.cs
new file mode 100644
index 0000000000..fee3ccb9fe
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Buffer.Unix.cs
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#if BIT64
+using nuint = System.UInt64;
+#else
+using nuint = System.UInt32;
+#endif
+
+namespace System
+{
+ public static partial class Buffer
+ {
+#if ARM64
+ // Managed code is currently faster than glibc unoptimized memmove
+ // TODO-ARM64-UNIX-OPT revisit when glibc optimized memmove is in Linux distros
+ // https://github.com/dotnet/coreclr/issues/13844
+ private const nuint MemmoveNativeThreshold = ulong.MaxValue;
+#elif ARM
+ private const nuint MemmoveNativeThreshold = 512;
+#else
+ private const nuint MemmoveNativeThreshold = 2048;
+#endif
+ }
+} \ No newline at end of file
diff --git a/src/System.Private.CoreLib/shared/System/Buffer.Windows.cs b/src/System.Private.CoreLib/shared/System/Buffer.Windows.cs
new file mode 100644
index 0000000000..4de884d68d
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Buffer.Windows.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#if BIT64
+using nuint = System.UInt64;
+#else
+using nuint = System.UInt32;
+#endif
+
+namespace System
+{
+ public static partial class Buffer
+ {
+#if ARM64
+ // Determine optimal value for Windows.
+ // https://github.com/dotnet/coreclr/issues/13843
+ private const nuint MemmoveNativeThreshold = ulong.MaxValue;
+#else
+ private const nuint MemmoveNativeThreshold = 2048;
+#endif
+ }
+} \ No newline at end of file
diff --git a/src/System.Private.CoreLib/shared/System/Buffer.cs b/src/System.Private.CoreLib/shared/System/Buffer.cs
index dda25b827f..f2ffaaea85 100644
--- a/src/System.Private.CoreLib/shared/System/Buffer.cs
+++ b/src/System.Private.CoreLib/shared/System/Buffer.cs
@@ -142,23 +142,6 @@ namespace System
// This method has different signature for x64 and other platforms and is done for performance reasons.
internal static unsafe void Memmove(byte* dest, byte* src, nuint len)
{
-#if AMD64 || (BIT32 && !ARM)
- const nuint CopyThreshold = 2048;
-#elif ARM64
-#if PLATFORM_WINDOWS
- // Determined optimal value for Windows.
- // https://github.com/dotnet/coreclr/issues/13843
- const nuint CopyThreshold = ulong.MaxValue;
-#else // PLATFORM_WINDOWS
- // Managed code is currently faster than glibc unoptimized memmove
- // TODO-ARM64-UNIX-OPT revisit when glibc optimized memmove is in Linux distros
- // https://github.com/dotnet/coreclr/issues/13844
- const nuint CopyThreshold = ulong.MaxValue;
-#endif // PLATFORM_WINDOWS
-#else
- const nuint CopyThreshold = 512;
-#endif // AMD64 || (BIT32 && !ARM)
-
// P/Invoke into the native version when the buffers are overlapping.
if (((nuint)dest - (nuint)src < len) || ((nuint)src - (nuint)dest < len))
{
@@ -260,14 +243,14 @@ namespace System
MCPY05:
// PInvoke to the native version when the copy length exceeds the threshold.
- if (len > CopyThreshold)
+ if (len > MemmoveNativeThreshold)
{
goto PInvoke;
}
// Copy 64-bytes at a time until the remainder is less than 64.
// If remainder is greater than 16 bytes, then jump to MCPY00. Otherwise, unconditionally copy the last 16 bytes and return.
- Debug.Assert(len > 64 && len <= CopyThreshold);
+ Debug.Assert(len > 64 && len <= MemmoveNativeThreshold);
nuint n = len >> 6;
MCPY06:
@@ -356,23 +339,6 @@ namespace System
// This method has different signature for x64 and other platforms and is done for performance reasons.
private static void Memmove(ref byte dest, ref byte src, nuint len)
{
-#if AMD64 || (BIT32 && !ARM)
- const nuint CopyThreshold = 2048;
-#elif ARM64
-#if PLATFORM_WINDOWS
- // Determined optimal value for Windows.
- // https://github.com/dotnet/coreclr/issues/13843
- const nuint CopyThreshold = ulong.MaxValue;
-#else // PLATFORM_WINDOWS
- // Managed code is currently faster than glibc unoptimized memmove
- // TODO-ARM64-UNIX-OPT revisit when glibc optimized memmove is in Linux distros
- // https://github.com/dotnet/coreclr/issues/13844
- const nuint CopyThreshold = ulong.MaxValue;
-#endif // PLATFORM_WINDOWS
-#else
- const nuint CopyThreshold = 512;
-#endif // AMD64 || (BIT32 && !ARM)
-
// P/Invoke into the native version when the buffers are overlapping.
if (((nuint)Unsafe.ByteOffset(ref src, ref dest) < len) || ((nuint)Unsafe.ByteOffset(ref dest, ref src) < len))
{
@@ -484,14 +450,14 @@ namespace System
MCPY05:
// PInvoke to the native version when the copy length exceeds the threshold.
- if (len > CopyThreshold)
+ if (len > MemmoveNativeThreshold)
{
goto PInvoke;
}
// Copy 64-bytes at a time until the remainder is less than 64.
// If remainder is greater than 16 bytes, then jump to MCPY00. Otherwise, unconditionally copy the last 16 bytes and return.
- Debug.Assert(len > 64 && len <= CopyThreshold);
+ Debug.Assert(len > 64 && len <= MemmoveNativeThreshold);
nuint n = len >> 6;
MCPY06:
diff --git a/src/System.Private.CoreLib/shared/System/Collections/ObjectModel/Collection.cs b/src/System.Private.CoreLib/shared/System/Collections/ObjectModel/Collection.cs
index 5d6c9c9757..4898c0cae2 100644
--- a/src/System.Private.CoreLib/shared/System/Collections/ObjectModel/Collection.cs
+++ b/src/System.Private.CoreLib/shared/System/Collections/ObjectModel/Collection.cs
@@ -49,7 +49,7 @@ namespace System.Collections.ObjectModel
ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
}
- if (index < 0 || index >= items.Count)
+ if ((uint)index >= (uint)items.Count)
{
ThrowHelper.ThrowArgumentOutOfRange_IndexException();
}
@@ -108,9 +108,9 @@ namespace System.Collections.ObjectModel
ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
}
- if (index < 0 || index > items.Count)
+ if ((uint)index > (uint)items.Count)
{
- ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_ListInsert);
+ ThrowHelper.ThrowArgumentOutOfRange_IndexException();
}
InsertItem(index, item);
@@ -125,12 +125,12 @@ namespace System.Collections.ObjectModel
if (collection == null)
{
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.list);
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
}
if ((uint)index > (uint)items.Count)
{
- ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_ListInsert);
+ ThrowHelper.ThrowArgumentOutOfRange_IndexException();
}
InsertItemsRange(index, collection);
@@ -198,7 +198,7 @@ namespace System.Collections.ObjectModel
if (collection == null)
{
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.list);
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
}
ReplaceItemsRange(index, count, collection);
@@ -211,7 +211,7 @@ namespace System.Collections.ObjectModel
ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
}
- if (index < 0 || index >= items.Count)
+ if ((uint)index >= (uint)items.Count)
{
ThrowHelper.ThrowArgumentOutOfRange_IndexException();
}
diff --git a/src/System.Private.CoreLib/shared/System/CurrentSystemTimeZone.cs b/src/System.Private.CoreLib/shared/System/CurrentSystemTimeZone.cs
index 0112c81abf..207da21835 100644
--- a/src/System.Private.CoreLib/shared/System/CurrentSystemTimeZone.cs
+++ b/src/System.Private.CoreLib/shared/System/CurrentSystemTimeZone.cs
@@ -209,7 +209,7 @@ namespace System
return (DaylightTime)m_CachedDaylightChanges[objYear];
}
- // The per-year information is cached in in this instance value. As a result it can
+ // The per-year information is cached in this instance value. As a result it can
// be cleaned up by CultureInfo.ClearCachedData, which will clear the instance of this object
private readonly Hashtable m_CachedDaylightChanges = new Hashtable();
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerBrowsableAttribute.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerBrowsableAttribute.cs
index b9d62225a9..8a53052b6f 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerBrowsableAttribute.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/DebuggerBrowsableAttribute.cs
@@ -5,7 +5,7 @@
namespace System.Diagnostics
{
// DebuggerBrowsableState states are defined as follows:
- // Never never show this element
+ // Never element should never show
// Expanded expansion of the class is done, so that all visible internal members are shown
// Collapsed expansion of the class is not performed. Internal visible members are hidden
// RootHidden The target element itself should not be shown, but should instead be
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/ActivityTracker.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/ActivityTracker.cs
index 70ebafcd69..d0cd84a6ae 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/ActivityTracker.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/ActivityTracker.cs
@@ -590,7 +590,7 @@ namespace System.Diagnostics.Tracing
/// while that task is running. Thus m_current 'flows' to any task that is caused by the current thread that
/// last set it.
///
- /// This variable points a a linked list that represents all Activities that have started but have not stopped.
+ /// This variable points to a linked list that represents all Activities that have started but have not stopped.
/// </summary>
AsyncLocal<ActivityInfo> m_current;
bool m_checkedForEnable;
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs
index 3a18dce13f..95ac1da80d 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/EventSource.cs
@@ -2085,7 +2085,7 @@ namespace System.Diagnostics.Tracing
SendManifest(manifestBuilder.CreateManifest());
}
- // We use this low level routine to to bypass the enabled checking, since the eventSource itself is only partially inited.
+ // We use this low level routine to bypass the enabled checking, since the eventSource itself is only partially inited.
fixed (char* msgStringPtr = msgString)
{
EventDescriptor descr = new EventDescriptor(0, 0, 0, (byte)level, 0, 0, keywords);
@@ -3875,7 +3875,7 @@ namespace System.Diagnostics.Tracing
break;
if (cur == this)
{
- // Found our Listener, remove references to to it in the eventSources
+ // Found our Listener, remove references to it in the eventSources
prev.m_Next = cur.m_Next; // Remove entry.
RemoveReferencesToListenerInEventSources(cur);
break;
@@ -5369,7 +5369,7 @@ namespace System.Diagnostics.Tracing
// support). The manifest generated *MUST* have the channels specified in the same order (that's how our computed keywords are mapped
// to channels by the OS infrastructure).
// If channelKeyworkds is present, and has keywords bits in the ValidPredefinedChannelKeywords then it is
- // assumed that that the keyword for that channel should be that bit.
+ // assumed that the keyword for that channel should be that bit.
// otherwise we allocate a channel bit for the channel.
// explicit channel bits are only used by WCF to mimic an existing manifest,
// so we don't dont do error checking.
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingEventCounter.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingEventCounter.cs
index 1b83819479..569c9a0113 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingEventCounter.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingEventCounter.cs
@@ -62,7 +62,7 @@ namespace System.Diagnostics.Tracing
IncrementingCounterPayload payload = new IncrementingCounterPayload();
payload.Name = _name;
payload.IntervalSec = intervalSec;
- payload.DisplayName = (DisplayName == null) ? "" : DisplayName;
+ payload.DisplayName = DisplayName ?? "";
payload.DisplayRateTimeScale = (DisplayRateTimeScale == TimeSpan.Zero) ? "" : DisplayRateTimeScale.ToString("c");
payload.MetaData = GetMetaDataString();
payload.Increment = _increment - _prevIncrement;
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingPollingCounter.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingPollingCounter.cs
index 8cd5c42b1b..b5a2582083 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingPollingCounter.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/IncrementingPollingCounter.cs
@@ -71,7 +71,7 @@ namespace System.Diagnostics.Tracing
{
IncrementingCounterPayload payload = new IncrementingCounterPayload();
payload.Name = _name;
- payload.DisplayName = (DisplayName == null) ? "" : DisplayName;
+ payload.DisplayName = DisplayName ?? "";
payload.DisplayRateTimeScale = (DisplayRateTimeScale == TimeSpan.Zero) ? "" : DisplayRateTimeScale.ToString("c");
payload.IntervalSec = intervalSec;
payload.MetaData = GetMetaDataString();
diff --git a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/PollingCounter.cs b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/PollingCounter.cs
index f156ef1dd0..695b357f45 100644
--- a/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/PollingCounter.cs
+++ b/src/System.Private.CoreLib/shared/System/Diagnostics/Tracing/PollingCounter.cs
@@ -58,7 +58,7 @@ namespace System.Diagnostics.Tracing
CounterPayload payload = new CounterPayload();
payload.Name = _name;
- payload.DisplayName = (DisplayName == null) ? "" : DisplayName;
+ payload.DisplayName = DisplayName ?? "";
payload.Count = 1; // NOTE: These dumb-looking statistics is intentional
payload.IntervalSec = intervalSec;
payload.Mean = value;
diff --git a/src/System.Private.CoreLib/shared/System/Enum.cs b/src/System.Private.CoreLib/shared/System/Enum.cs
index 444367cebf..94ca919a35 100644
--- a/src/System.Private.CoreLib/shared/System/Enum.cs
+++ b/src/System.Private.CoreLib/shared/System/Enum.cs
@@ -11,6 +11,7 @@ using Internal.Runtime.CompilerServices;
#if CORERT
using CorElementType = System.Runtime.RuntimeImports.RhCorElementType;
using RuntimeType = System.Type;
+using EnumInfo = Internal.Runtime.Augments.EnumInfo;
#endif
// The code below includes partial support for float/double and
@@ -132,52 +133,47 @@ namespace System
}
}
- internal static string GetEnumName(RuntimeType eT, ulong ulValue)
+ internal static string GetEnumName(RuntimeType enumType, ulong ulValue)
{
- Debug.Assert(eT != null);
- ulong[] ulValues = InternalGetValues(eT);
- int index = Array.BinarySearch(ulValues, ulValue);
+ return GetEnumName(GetEnumInfo(enumType), ulValue);
+ }
+ private static string GetEnumName(EnumInfo enumInfo, ulong ulValue)
+ {
+ int index = Array.BinarySearch(enumInfo.Values, ulValue);
if (index >= 0)
{
- string[] names = InternalGetNames(eT);
- return names[index];
+ return enumInfo.Names[index];
}
return null; // return null so the caller knows to .ToString() the input
}
- private static string InternalFormat(RuntimeType eT, ulong value)
+ private static string InternalFormat(RuntimeType enumType, ulong value)
{
- Debug.Assert(eT != null);
-
- // These values are sorted by value. Don't change this
- TypeValuesAndNames entry = GetCachedValuesAndNames(eT, true);
+ EnumInfo enumInfo = GetEnumInfo(enumType);
- if (!entry.IsFlag) // Not marked with Flags attribute
+ if (!enumInfo.HasFlagsAttribute)
{
- return GetEnumName(eT, value);
+ return GetEnumName(enumInfo, value);
}
else // These are flags OR'ed together (We treat everything as unsigned types)
{
- return InternalFlagsFormat(eT, entry, value);
+ return InternalFlagsFormat(enumType, enumInfo, value);
}
}
- private static string InternalFlagsFormat(RuntimeType eT, ulong result)
+ private static string InternalFlagsFormat(RuntimeType enumType, ulong result)
{
- // These values are sorted by value. Don't change this
- TypeValuesAndNames entry = GetCachedValuesAndNames(eT, true);
-
- return InternalFlagsFormat(eT, entry, result);
+ return InternalFlagsFormat(enumType, GetEnumInfo(enumType), result);
}
- private static string InternalFlagsFormat(RuntimeType eT, TypeValuesAndNames entry, ulong resultValue)
+ private static string InternalFlagsFormat(RuntimeType enumType, EnumInfo enumInfo, ulong resultValue)
{
- Debug.Assert(eT != null);
+ Debug.Assert(enumType != null);
- string[] names = entry.Names;
- ulong[] values = entry.Values;
+ string[] names = enumInfo.Names;
+ ulong[] values = enumInfo.Values;
Debug.Assert(names.Length == values.Length);
// Values are sorted, so if the incoming value is 0, we can check to see whether
@@ -700,9 +696,9 @@ namespace System
private static bool TryParseByName(RuntimeType enumType, string originalValueString, ReadOnlySpan<char> value, bool ignoreCase, bool throwOnFailure, out ulong result)
{
// Find the field. Let's assume that these are always static classes because the class is an enum.
- TypeValuesAndNames entry = GetCachedValuesAndNames(enumType, getNames: true);
- string[] enumNames = entry.Names;
- ulong[] enumValues = entry.Values;
+ EnumInfo enumInfo = GetEnumInfo(enumType);
+ string[] enumNames = enumInfo.Names;
+ ulong[] enumValues = enumInfo.Values;
bool parsed = true;
ulong localResult = 0;
@@ -782,18 +778,6 @@ namespace System
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool StartsNumber(char c) => char.IsInRange(c, '0', '9') || c == '-' || c == '+';
- internal static ulong[] InternalGetValues(RuntimeType enumType)
- {
- // Get all of the values
- return GetCachedValuesAndNames(enumType, false).Values;
- }
-
- internal static string[] InternalGetNames(RuntimeType enumType)
- {
- // Get all of the names
- return GetCachedValuesAndNames(enumType, true).Names;
- }
-
public static object ToObject(Type enumType, object value)
{
if (value == null)
@@ -840,14 +824,6 @@ namespace System
}
}
- public static bool IsDefined(Type enumType, object value)
- {
- if (enumType == null)
- throw new ArgumentNullException(nameof(enumType));
-
- return enumType.IsEnumDefined(value);
- }
-
public static string Format(Type enumType, object value, string format)
{
RuntimeType rtType = ValidateRuntimeType(enumType);
@@ -906,23 +882,6 @@ namespace System
}
#endregion
- #region Definitions
- private class TypeValuesAndNames
- {
- public readonly bool IsFlag;
- public readonly ulong[] Values;
- public readonly string[] Names;
-
- // Each entry contains a list of sorted pair of enum field names and values, sorted by values
- public TypeValuesAndNames(bool isFlag, ulong[] values, string[] names)
- {
- IsFlag = isFlag;
- Values = values;
- Names = names;
- }
- }
- #endregion
-
#region Private Methods
internal object GetValue()
{
@@ -1066,39 +1025,6 @@ namespace System
}
#endregion
- #region IComparable
- public int CompareTo(object target)
- {
- const int retIncompatibleMethodTables = 2; // indicates that the method tables did not match
- const int retInvalidEnumType = 3; // indicates that the enum was of an unknown/unsupported underlying type
-
- if (this == null)
- throw new NullReferenceException();
-
- int ret = InternalCompareTo(this, target);
-
- if (ret < retIncompatibleMethodTables)
- {
- // -1, 0 and 1 are the normal return codes
- return ret;
- }
- else if (ret == retIncompatibleMethodTables)
- {
- Type thisType = this.GetType();
- Type targetType = target.GetType();
-
- throw new ArgumentException(SR.Format(SR.Arg_EnumAndObjectMustBeSameType, targetType, thisType));
- }
- else
- {
- // assert valid return code (3)
- Debug.Assert(ret == retInvalidEnumType, "Enum.InternalCompareTo return code was invalid");
-
- throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
- }
- }
- #endregion
-
#region Public Methods
public string ToString(string format)
{
diff --git a/src/System.Private.CoreLib/shared/System/Exception.cs b/src/System.Private.CoreLib/shared/System/Exception.cs
new file mode 100644
index 0000000000..764f36a2fc
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Exception.cs
@@ -0,0 +1,196 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections;
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
+ public partial class Exception : ISerializable
+ {
+ public Exception()
+ {
+ _HResult = HResults.COR_E_EXCEPTION;
+ }
+
+ public Exception(string message)
+ : this()
+ {
+ _message = message;
+ }
+
+ // Creates a new Exception. All derived classes should
+ // provide this constructor.
+ // Note: the stack trace is not started until the exception
+ // is thrown
+ //
+ public Exception(string message, Exception innerException)
+ : this()
+ {
+ _message = message;
+ _innerException = innerException;
+ }
+
+ protected Exception(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ throw new ArgumentNullException(nameof(info));
+
+ _message = info.GetString("Message"); // Do not rename (binary serialization)
+ _data = (IDictionary)(info.GetValueNoThrow("Data", typeof(IDictionary))); // Do not rename (binary serialization)
+ _innerException = (Exception)(info.GetValue("InnerException", typeof(Exception))); // Do not rename (binary serialization)
+ _helpURL = info.GetString("HelpURL"); // Do not rename (binary serialization)
+ _stackTraceString = info.GetString("StackTraceString"); // Do not rename (binary serialization)
+ _HResult = info.GetInt32("HResult"); // Do not rename (binary serialization)
+ _source = info.GetString("Source"); // Do not rename (binary serialization)
+
+ RestoreRemoteStackTrace(info, context);
+ }
+
+ public virtual string Message
+ {
+ get
+ {
+ return _message ?? SR.Format(SR.Exception_WasThrown, GetClassName());
+ }
+ }
+
+ public virtual IDictionary Data
+ {
+ get
+ {
+ return _data ?? (_data = CreateDataContainer());
+ }
+ }
+
+ private string GetClassName() => GetType().ToString();
+
+ // Retrieves the lowest exception (inner most) for the given Exception.
+ // This will traverse exceptions using the innerException property.
+ public virtual Exception GetBaseException()
+ {
+ Exception inner = InnerException;
+ Exception back = this;
+
+ while (inner != null)
+ {
+ back = inner;
+ inner = inner.InnerException;
+ }
+
+ return back;
+ }
+
+ public Exception InnerException => _innerException;
+
+ // Sets the help link for this exception.
+ // This should be in a URL/URN form, such as:
+ // "file:///C:/Applications/Bazzal/help.html#ErrorNum42"
+ public virtual string HelpLink
+ {
+ get
+ {
+ return _helpURL;
+ }
+ set
+ {
+ _helpURL = value;
+ }
+ }
+
+ public virtual string Source
+ {
+ get
+ {
+ return _source ?? (_source = CreateSourceName());
+ }
+ set
+ {
+ _source = value;
+ }
+ }
+
+ public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ {
+ throw new ArgumentNullException(nameof(info));
+ }
+
+ if (_source == null)
+ {
+ _source = Source; // Set the Source information correctly before serialization
+ }
+
+ info.AddValue("ClassName", GetClassName(), typeof(string)); // Do not rename (binary serialization)
+ info.AddValue("Message", _message, typeof(string)); // Do not rename (binary serialization)
+ info.AddValue("Data", _data, typeof(IDictionary)); // Do not rename (binary serialization)
+ info.AddValue("InnerException", _innerException, typeof(Exception)); // Do not rename (binary serialization)
+ info.AddValue("HelpURL", _helpURL, typeof(string)); // Do not rename (binary serialization)
+ info.AddValue("StackTraceString", SerializationStackTraceString, typeof(string)); // Do not rename (binary serialization)
+ info.AddValue("RemoteStackTraceString", SerializationRemoteStackTraceString, typeof(string)); // Do not rename (binary serialization)
+ info.AddValue("RemoteStackIndex", 0, typeof(int)); // Do not rename (binary serialization)
+ info.AddValue("ExceptionMethod", null, typeof(string)); // Do not rename (binary serialization)
+ info.AddValue("HResult", _HResult); // Do not rename (binary serialization)
+ info.AddValue("Source", _source, typeof(string)); // Do not rename (binary serialization)
+ info.AddValue("WatsonBuckets", SerializationWatsonBuckets, typeof(byte[])); // Do not rename (binary serialization)
+ }
+
+ public override string ToString()
+ {
+ return ToString(true, true);
+ }
+
+ private string ToString(bool needFileLineInfo, bool needMessage)
+ {
+ string s = GetClassName();
+
+ string message = (needMessage ? Message : null);
+ if (!string.IsNullOrEmpty(message))
+ {
+ s += ": " + message;
+ }
+
+ if (_innerException != null)
+ {
+ s = s + " ---> " + _innerException.ToString(needFileLineInfo, needMessage) + Environment.NewLine +
+ " " + SR.Exception_EndOfInnerExceptionStack;
+ }
+
+ string stackTrace = GetStackTrace(needFileLineInfo);
+ if (stackTrace != null)
+ {
+ s += Environment.NewLine + stackTrace;
+ }
+
+ return s;
+ }
+
+ protected event EventHandler<SafeSerializationEventArgs> SerializeObjectState
+ {
+ add { throw new PlatformNotSupportedException(SR.PlatformNotSupported_SecureBinarySerialization); }
+ remove { throw new PlatformNotSupportedException(SR.PlatformNotSupported_SecureBinarySerialization); }
+ }
+
+ public int HResult
+ {
+ get
+ {
+ return _HResult;
+ }
+ set
+ {
+ _HResult = value;
+ }
+ }
+
+ // this method is required so Object.GetType is not made virtual by the compiler
+ // _Exception.GetType()
+ public new Type GetType() => base.GetType();
+
+ partial void RestoreRemoteStackTrace(SerializationInfo info, StreamingContext context);
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/CultureInfo.cs b/src/System.Private.CoreLib/shared/System/Globalization/CultureInfo.cs
index 01e5756e86..1b22676bad 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/CultureInfo.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/CultureInfo.cs
@@ -1126,7 +1126,7 @@ namespace System.Globalization
}
/// <summary>
- /// Helper function both both overloads of GetCachedReadOnlyCulture. If lcid is 0, we use the name.
+ /// Helper function overloads of GetCachedReadOnlyCulture. If lcid is 0, we use the name.
/// If lcid is -1, use the altName and create one of those special SQL cultures.
/// </summary>
internal static CultureInfo GetCultureInfoHelper(int lcid, string name, string altName)
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/DateTimeParse.cs b/src/System.Private.CoreLib/shared/System/Globalization/DateTimeParse.cs
index bc0193d685..99cc413e26 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/DateTimeParse.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/DateTimeParse.cs
@@ -3690,7 +3690,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR,
// DateTimeOffset.Parse should allow dates without a year, but only if there is also no time zone marker;
// e.g. "May 1 5pm" is OK, but "May 1 5pm -08:30" is not. This is somewhat pragmatic, since we would
// have to rearchitect parsing completely to allow this one case to correctly handle things like leap
- // years and leap months. Is is an extremely corner case, and DateTime is basically incorrect in that
+ // years and leap months. Is an extremely corner case, and DateTime is basically incorrect in that
// case today.
//
// values like "11:00Z" or "11:00 -3:00" are also acceptable
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/EastAsianLunisolarCalendar.cs b/src/System.Private.CoreLib/shared/System/Globalization/EastAsianLunisolarCalendar.cs
index 00398ca021..d1ca0f8d74 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/EastAsianLunisolarCalendar.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/EastAsianLunisolarCalendar.cs
@@ -619,7 +619,7 @@ namespace System.Globalization
/// <summary>
/// Returns the leap month in a calendar year of the specified era. This method returns 0
- /// if this this year is not a leap year.
+ /// if this year is not a leap year.
/// </summary>
public override int GetLeapMonth(int year, int era)
{
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/HebrewCalendar.cs b/src/System.Private.CoreLib/shared/System/Globalization/HebrewCalendar.cs
index dcab7e7158..67b3966e4e 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/HebrewCalendar.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/HebrewCalendar.cs
@@ -457,7 +457,7 @@ namespace System.Globalization
DateBuffer lunarDate = new DateBuffer(); // lunar month and day for Jan 1
- // From the table looking-up value of HebrewTable[index] (stored in lunarDate.day), we get the the
+ // From the table looking-up value of HebrewTable[index] (stored in lunarDate.day), we get the
// lunar month and lunar day where the Gregorian date 1/1 falls.
lunarDate.year = gregorianYear + HebrewYearOf1AD;
hebrewYearType = GetLunarMonthDay(gregorianYear, lunarDate);
diff --git a/src/System.Private.CoreLib/shared/System/IO/FileStream.Win32.cs b/src/System.Private.CoreLib/shared/System/IO/FileStream.Win32.cs
index edcf8e546e..1c371d54e1 100644
--- a/src/System.Private.CoreLib/shared/System/IO/FileStream.Win32.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/FileStream.Win32.cs
@@ -67,7 +67,7 @@ namespace System.IO
switch (status)
{
case 0:
- // We we're successful
+ // We were successful
break;
case Interop.NtDll.STATUS_INVALID_HANDLE:
if (!ignoreInvalid)
diff --git a/src/System.Private.CoreLib/shared/System/Index.cs b/src/System.Private.CoreLib/shared/System/Index.cs
index 9767b981ef..62d7f3440c 100644
--- a/src/System.Private.CoreLib/shared/System/Index.cs
+++ b/src/System.Private.CoreLib/shared/System/Index.cs
@@ -103,13 +103,15 @@ namespace System
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetOffset(int length)
{
- int offset;
-
+ int offset = _value;
if (IsFromEnd)
- offset = length - (~_value);
- else
- offset = _value;
+ {
+ // offset = length - (~value)
+ // offset = length + (~(~value) + 1)
+ // offset = length + value + 1
+ offset += length + 1;
+ }
return offset;
}
diff --git a/src/System.Private.CoreLib/shared/System/Memory.cs b/src/System.Private.CoreLib/shared/System/Memory.cs
index ba31a6aeae..2074404630 100644
--- a/src/System.Private.CoreLib/shared/System/Memory.cs
+++ b/src/System.Private.CoreLib/shared/System/Memory.cs
@@ -6,6 +6,7 @@ using System.Buffers;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Text;
using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
@@ -164,7 +165,13 @@ namespace System
// No validation performed in release builds; caller must provide any necessary validation.
// 'obj is T[]' below also handles things like int[] <-> uint[] being convertible
- Debug.Assert((obj == null) || (typeof(T) == typeof(char) && obj is string) || (obj is T[]) || (obj is MemoryManager<T>));
+ Debug.Assert((obj == null)
+ || (typeof(T) == typeof(char) && obj is string)
+#if FEATURE_UTF8STRING
+ || ((typeof(T) == typeof(byte) || typeof(T) == typeof(Char8)) && obj is Utf8String)
+#endif // FEATURE_UTF8STRING
+ || (obj is T[])
+ || (obj is MemoryManager<T>));
_object = obj;
_index = start;
@@ -212,6 +219,14 @@ namespace System
{
return (_object is string str) ? str.Substring(_index, _length) : Span.ToString();
}
+#if FEATURE_UTF8STRING
+ else if (typeof(T) == typeof(Char8))
+ {
+ // TODO_UTF8STRING: Call into optimized transcoding routine when it's available.
+ Span<T> span = Span;
+ return Encoding.UTF8.GetString(new ReadOnlySpan<byte>(ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)), span.Length));
+ }
+#endif // FEATURE_UTF8STRING
return string.Format("System.Memory<{0}>[{1}]", typeof(T).Name, _length);
}
@@ -317,6 +332,13 @@ namespace System
refToReturn = ref Unsafe.As<char, T>(ref Unsafe.As<string>(tmpObject).GetRawStringData());
lengthOfUnderlyingSpan = Unsafe.As<string>(tmpObject).Length;
}
+#if FEATURE_UTF8STRING
+ else if ((typeof(T) == typeof(byte) || typeof(T) == typeof(Char8)) && tmpObject.GetType() == typeof(Utf8String))
+ {
+ refToReturn = ref Unsafe.As<byte, T>(ref Unsafe.As<Utf8String>(tmpObject).DangerousGetMutableReference());
+ lengthOfUnderlyingSpan = Unsafe.As<Utf8String>(tmpObject).Length;
+ }
+#endif // FEATURE_UTF8STRING
else if (RuntimeHelpers.ObjectHasComponentSize(tmpObject))
{
// We know the object is not null, it's not a string, and it is variable-length. The only
@@ -427,6 +449,14 @@ namespace System
ref char stringData = ref Unsafe.Add(ref s.GetRawStringData(), _index);
return new MemoryHandle(Unsafe.AsPointer(ref stringData), handle);
}
+#if FEATURE_UTF8STRING
+ else if ((typeof(T) == typeof(byte) || typeof(T) == typeof(Char8)) && tmpObject is Utf8String utf8String)
+ {
+ GCHandle handle = GCHandle.Alloc(tmpObject, GCHandleType.Pinned);
+ ref byte stringData = ref utf8String.DangerousGetMutableReference(_index);
+ return new MemoryHandle(Unsafe.AsPointer(ref stringData), handle);
+ }
+#endif // FEATURE_UTF8STRING
else if (RuntimeHelpers.ObjectHasComponentSize(tmpObject))
{
// 'tmpObject is T[]' below also handles things like int[] <-> uint[] being convertible
diff --git a/src/System.Private.CoreLib/shared/System/MemoryExtensions.Trim.cs b/src/System.Private.CoreLib/shared/System/MemoryExtensions.Trim.cs
new file mode 100644
index 0000000000..be98bb1fba
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/MemoryExtensions.Trim.cs
@@ -0,0 +1,866 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+
+namespace System
+{
+ public static partial class MemoryExtensions
+ {
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a specified element from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static Memory<T> Trim<T>(this Memory<T> memory, T trimElement)
+ where T : IEquatable<T>
+ {
+ ReadOnlySpan<T> span = memory.Span;
+ int start = ClampStart(span, trimElement);
+ int length = ClampEnd(span, start + 1, trimElement);
+ return memory.Slice(start, length);
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a specified element from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static Memory<T> TrimStart<T>(this Memory<T> memory, T trimElement)
+ where T : IEquatable<T>
+ => memory.Slice(ClampStart(memory.Span, trimElement));
+
+ /// <summary>
+ /// Removes all trailing occurrences of a specified element from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static Memory<T> TrimEnd<T>(this Memory<T> memory, T trimElement)
+ where T : IEquatable<T>
+ => memory.Slice(0, ClampEnd(memory.Span, 0, trimElement));
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a specified element from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static ReadOnlyMemory<T> Trim<T>(this ReadOnlyMemory<T> memory, T trimElement)
+ where T : IEquatable<T>
+ {
+ ReadOnlySpan<T> span = memory.Span;
+ int start = ClampStart(span, trimElement);
+ int length = ClampEnd(span, start + 1, trimElement);
+ return memory.Slice(start, length);
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a specified element from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static ReadOnlyMemory<T> TrimStart<T>(this ReadOnlyMemory<T> memory, T trimElement)
+ where T : IEquatable<T>
+ => memory.Slice(ClampStart(memory.Span, trimElement));
+
+ /// <summary>
+ /// Removes all trailing occurrences of a specified element from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static ReadOnlyMemory<T> TrimEnd<T>(this ReadOnlyMemory<T> memory, T trimElement)
+ where T : IEquatable<T>
+ => memory.Slice(0, ClampEnd(memory.Span, 0, trimElement));
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static Span<T> Trim<T>(this Span<T> span, T trimElement)
+ where T : IEquatable<T>
+ {
+ int start = ClampStart(span, trimElement);
+ int length = ClampEnd(span, start + 1, trimElement);
+ return span.Slice(start, length);
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static Span<T> TrimStart<T>(this Span<T> span, T trimElement)
+ where T : IEquatable<T>
+ => span.Slice(ClampStart(span, trimElement));
+
+ /// <summary>
+ /// Removes all trailing occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static Span<T> TrimEnd<T>(this Span<T> span, T trimElement)
+ where T : IEquatable<T>
+ => span.Slice(0, ClampEnd(span, 0, trimElement));
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static ReadOnlySpan<T> Trim<T>(this ReadOnlySpan<T> span, T trimElement)
+ where T : IEquatable<T>
+ {
+ int start = ClampStart(span, trimElement);
+ int length = ClampEnd(span, start + 1, trimElement);
+ return span.Slice(start, length);
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static ReadOnlySpan<T> TrimStart<T>(this ReadOnlySpan<T> span, T trimElement)
+ where T : IEquatable<T>
+ => span.Slice(ClampStart(span, trimElement));
+
+ /// <summary>
+ /// Removes all trailing occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ public static ReadOnlySpan<T> TrimEnd<T>(this ReadOnlySpan<T> span, T trimElement)
+ where T : IEquatable<T>
+ => span.Slice(0, ClampEnd(span, 0, trimElement));
+
+ /// <summary>
+ /// Delimits all leading occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ private static int ClampStart<T>(ReadOnlySpan<T> span, T trimElement)
+ where T : IEquatable<T>
+ {
+ int start = 0;
+
+ if (trimElement != null)
+ {
+ for (; start < span.Length; start++)
+ {
+ if (!trimElement.Equals(span[start]))
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (; start < span.Length; start++)
+ {
+ if (span[start] != null)
+ {
+ break;
+ }
+ }
+ }
+
+ return start;
+ }
+
+ /// <summary>
+ /// Delimits all trailing occurrences of a specified element from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the element is removed.</param>
+ /// <param name="start">The start index from which to being searching.</param>
+ /// <param name="trimElement">The specified element to look for and remove.</param>
+ private static int ClampEnd<T>(ReadOnlySpan<T> span, int start, T trimElement)
+ where T : IEquatable<T>
+ {
+ // Initially, start==len==0. If ClampStart trims all, start==len
+ Debug.Assert((uint)start <= span.Length);
+
+ int end = span.Length - 1;
+
+ if (trimElement != null)
+ {
+ for (; end >= start; end--)
+ {
+ if (!trimElement.Equals(span[end]))
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (; end >= start; end--)
+ {
+ if (span[end] != null)
+ {
+ break;
+ }
+ }
+ }
+
+ return end - start + 1;
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a set of elements specified
+ /// in a readonly span from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the memory is returned unaltered.</remarks>
+ public static Memory<T> Trim<T>(this Memory<T> memory, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ ReadOnlySpan<T> span = memory.Span;
+ int start = ClampStart(span, trimElements);
+ int length = ClampEnd(span, start + 1, trimElements);
+ return memory.Slice(start, length);
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return Trim(memory, trimElements[0]);
+ }
+
+ return memory;
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a set of elements specified
+ /// in a readonly span from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the memory is returned unaltered.</remarks>
+ public static Memory<T> TrimStart<T>(this Memory<T> memory, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return memory.Slice(ClampStart(memory.Span, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimStart(memory, trimElements[0]);
+ }
+
+ return memory;
+ }
+
+ /// <summary>
+ /// Removes all trailing occurrences of a set of elements specified
+ /// in a readonly span from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the memory is returned unaltered.</remarks>
+ public static Memory<T> TrimEnd<T>(this Memory<T> memory, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return memory.Slice(0, ClampEnd(memory.Span, 0, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimEnd(memory, trimElements[0]);
+ }
+
+ return memory;
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a set of elements specified
+ /// in a readonly span from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the memory is returned unaltered.</remarks>
+ public static ReadOnlyMemory<T> Trim<T>(this ReadOnlyMemory<T> memory, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ ReadOnlySpan<T> span = memory.Span;
+ int start = ClampStart(span, trimElements);
+ int length = ClampEnd(span, start + 1, trimElements);
+ return memory.Slice(start, length);
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return Trim(memory, trimElements[0]);
+ }
+
+ return memory;
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a set of elements specified
+ /// in a readonly span from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the memory is returned unaltered.</remarks>
+ public static ReadOnlyMemory<T> TrimStart<T>(this ReadOnlyMemory<T> memory, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return memory.Slice(ClampStart(memory.Span, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimStart(memory, trimElements[0]);
+ }
+
+ return memory;
+ }
+
+ /// <summary>
+ /// Removes all trailing occurrences of a set of elements specified
+ /// in a readonly span from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the memory is returned unaltered.</remarks>
+ public static ReadOnlyMemory<T> TrimEnd<T>(this ReadOnlyMemory<T> memory, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return memory.Slice(0, ClampEnd(memory.Span, 0, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimEnd(memory, trimElements[0]);
+ }
+
+ return memory;
+
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the span is returned unaltered.</remarks>
+ public static Span<T> Trim<T>(this Span<T> span, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ int start = ClampStart(span, trimElements);
+ int length = ClampEnd(span, start + 1, trimElements);
+ return span.Slice(start, length);
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return Trim(span, trimElements[0]);
+ }
+
+ return span;
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the span is returned unaltered.</remarks>
+ public static Span<T> TrimStart<T>(this Span<T> span, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return span.Slice(ClampStart(span, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimStart(span, trimElements[0]);
+ }
+
+ return span;
+ }
+
+ /// <summary>
+ /// Removes all trailing occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the span is returned unaltered.</remarks>
+ public static Span<T> TrimEnd<T>(this Span<T> span, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return span.Slice(0, ClampEnd(span, 0, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimEnd(span, trimElements[0]);
+ }
+
+ return span;
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the span is returned unaltered.</remarks>
+ public static ReadOnlySpan<T> Trim<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ int start = ClampStart(span, trimElements);
+ int length = ClampEnd(span, start + 1, trimElements);
+ return span.Slice(start, length);
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return Trim(span, trimElements[0]);
+ }
+
+ return span;
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the span is returned unaltered.</remarks>
+ public static ReadOnlySpan<T> TrimStart<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return span.Slice(ClampStart(span, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimStart(span, trimElements[0]);
+ }
+
+ return span;
+ }
+
+ /// <summary>
+ /// Removes all trailing occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ /// <remarks>If <paramref name="trimElements"/> is empty, the span is returned unaltered.</remarks>
+ public static ReadOnlySpan<T> TrimEnd<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ if (trimElements.Length > 1)
+ {
+ return span.Slice(0, ClampEnd(span, 0, trimElements));
+ }
+
+ if (trimElements.Length == 1)
+ {
+ return TrimEnd(span, trimElements[0]);
+ }
+
+ return span;
+ }
+
+ /// <summary>
+ /// Delimits all leading occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ private static int ClampStart<T>(ReadOnlySpan<T> span, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ int start = 0;
+ for (; start < span.Length; start++)
+ {
+ if (!trimElements.Contains(span[start]))
+ {
+ break;
+ }
+ }
+
+ return start;
+ }
+
+ /// <summary>
+ /// Delimits all trailing occurrences of a set of elements specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the elements are removed.</param>
+ /// <param name="start">The start index from which to being searching.</param>
+ /// <param name="trimElements">The span which contains the set of elements to remove.</param>
+ private static int ClampEnd<T>(ReadOnlySpan<T> span, int start, ReadOnlySpan<T> trimElements)
+ where T : IEquatable<T>
+ {
+ // Initially, start==len==0. If ClampStart trims all, start==len
+ Debug.Assert((uint)start <= span.Length);
+
+ int end = span.Length - 1;
+ for (; end >= start; end--)
+ {
+ if (!trimElements.Contains(span[end]))
+ {
+ break;
+ }
+ }
+
+ return end - start + 1;
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing white-space characters from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the characters are removed.</param>
+ public static Memory<char> Trim(this Memory<char> memory)
+ {
+ ReadOnlySpan<char> span = memory.Span;
+ int start = ClampStart(span);
+ int length = ClampEnd(span, start + 1);
+ return memory.Slice(start, length);
+ }
+
+ /// <summary>
+ /// Removes all leading white-space characters from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the characters are removed.</param>
+ public static Memory<char> TrimStart(this Memory<char> memory)
+ => memory.Slice(ClampStart(memory.Span));
+
+ /// <summary>
+ /// Removes all trailing white-space characters from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the characters are removed.</param>
+ public static Memory<char> TrimEnd(this Memory<char> memory)
+ => memory.Slice(0, ClampEnd(memory.Span, 0));
+
+ /// <summary>
+ /// Removes all leading and trailing white-space characters from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the characters are removed.</param>
+ public static ReadOnlyMemory<char> Trim(this ReadOnlyMemory<char> memory)
+ {
+ ReadOnlySpan<char> span = memory.Span;
+ int start = ClampStart(span);
+ int length = ClampEnd(span, start + 1);
+ return memory.Slice(start, length);
+ }
+
+ /// <summary>
+ /// Removes all leading white-space characters from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the characters are removed.</param>
+ public static ReadOnlyMemory<char> TrimStart(this ReadOnlyMemory<char> memory)
+ => memory.Slice(ClampStart(memory.Span));
+
+ /// <summary>
+ /// Removes all trailing white-space characters from the memory.
+ /// </summary>
+ /// <param name="memory">The source memory from which the characters are removed.</param>
+ public static ReadOnlyMemory<char> TrimEnd(this ReadOnlyMemory<char> memory)
+ => memory.Slice(0, ClampEnd(memory.Span, 0));
+
+ /// <summary>
+ /// Removes all leading and trailing white-space characters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> span)
+ {
+ int start = 0;
+ for (; start < span.Length; start++)
+ {
+ if (!char.IsWhiteSpace(span[start]))
+ {
+ break;
+ }
+ }
+
+ int end = span.Length - 1;
+ for (; end > start; end--)
+ {
+ if (!char.IsWhiteSpace(span[end]))
+ {
+ break;
+ }
+ }
+
+ return span.Slice(start, end - start + 1);
+ }
+
+ /// <summary>
+ /// Removes all leading white-space characters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ public static ReadOnlySpan<char> TrimStart(this ReadOnlySpan<char> span)
+ {
+ int start = 0;
+ for (; start < span.Length; start++)
+ {
+ if (!char.IsWhiteSpace(span[start]))
+ {
+ break;
+ }
+ }
+
+ return span.Slice(start);
+ }
+
+ /// <summary>
+ /// Removes all trailing white-space characters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ public static ReadOnlySpan<char> TrimEnd(this ReadOnlySpan<char> span)
+ {
+ int end = span.Length - 1;
+ for (; end >= 0; end--)
+ {
+ if (!char.IsWhiteSpace(span[end]))
+ {
+ break;
+ }
+ }
+
+ return span.Slice(0, end + 1);
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a specified character from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the character is removed.</param>
+ /// <param name="trimChar">The specified character to look for and remove.</param>
+ public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> span, char trimChar)
+ {
+ int start = 0;
+ for (; start < span.Length; start++)
+ {
+ if (span[start] != trimChar)
+ {
+ break;
+ }
+ }
+
+ int end = span.Length - 1;
+ for (; end > start; end--)
+ {
+ if (span[end] != trimChar)
+ {
+ break;
+ }
+ }
+
+ return span.Slice(start, end - start + 1);
+ }
+
+ /// <summary>
+ /// Removes all leading occurrences of a specified character from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the character is removed.</param>
+ /// <param name="trimChar">The specified character to look for and remove.</param>
+ public static ReadOnlySpan<char> TrimStart(this ReadOnlySpan<char> span, char trimChar)
+ {
+ int start = 0;
+ for (; start < span.Length; start++)
+ {
+ if (span[start] != trimChar)
+ {
+ break;
+ }
+ }
+
+ return span.Slice(start);
+ }
+
+ /// <summary>
+ /// Removes all trailing occurrences of a specified character from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the character is removed.</param>
+ /// <param name="trimChar">The specified character to look for and remove.</param>
+ public static ReadOnlySpan<char> TrimEnd(this ReadOnlySpan<char> span, char trimChar)
+ {
+ int end = span.Length - 1;
+ for (; end >= 0; end--)
+ {
+ if (span[end] != trimChar)
+ {
+ break;
+ }
+ }
+
+ return span.Slice(0, end + 1);
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing occurrences of a set of characters specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ /// <param name="trimChars">The span which contains the set of characters to remove.</param>
+ /// <remarks>If <paramref name="trimChars"/> is empty, white-space characters are removed instead.</remarks>
+ public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars)
+ => span.TrimStart(trimChars).TrimEnd(trimChars);
+
+ /// <summary>
+ /// Removes all leading occurrences of a set of characters specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ /// <param name="trimChars">The span which contains the set of characters to remove.</param>
+ /// <remarks>If <paramref name="trimChars"/> is empty, white-space characters are removed instead.</remarks>
+ public static ReadOnlySpan<char> TrimStart(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars)
+ {
+ if (trimChars.IsEmpty)
+ {
+ return span.TrimStart();
+ }
+
+ int start = 0;
+ for (; start < span.Length; start++)
+ {
+ for (int i = 0; i < trimChars.Length; i++)
+ {
+ if (span[start] == trimChars[i])
+ {
+ goto Next;
+ }
+ }
+
+ break;
+ Next:
+ ;
+ }
+
+ return span.Slice(start);
+ }
+
+ /// <summary>
+ /// Removes all trailing occurrences of a set of characters specified
+ /// in a readonly span from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ /// <param name="trimChars">The span which contains the set of characters to remove.</param>
+ /// <remarks>If <paramref name="trimChars"/> is empty, white-space characters are removed instead.</remarks>
+ public static ReadOnlySpan<char> TrimEnd(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars)
+ {
+ if (trimChars.IsEmpty)
+ {
+ return span.TrimEnd();
+ }
+
+ int end = span.Length - 1;
+ for (; end >= 0; end--)
+ {
+ for (int i = 0; i < trimChars.Length; i++)
+ {
+ if (span[end] == trimChars[i])
+ {
+ goto Next;
+ }
+ }
+
+ break;
+ Next:
+ ;
+ }
+
+ return span.Slice(0, end + 1);
+ }
+
+ /// <summary>
+ /// Removes all leading and trailing white-space characters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ public static Span<char> Trim(this Span<char> span)
+ {
+ int start = ClampStart(span);
+ int length = ClampEnd(span, start + 1);
+ return span.Slice(start, length);
+ }
+
+ /// <summary>
+ /// Removes all leading white-space characters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ public static Span<char> TrimStart(this Span<char> span)
+ => span.Slice(ClampStart(span));
+
+ /// <summary>
+ /// Removes all trailing white-space characters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ public static Span<char> TrimEnd(this Span<char> span)
+ => span.Slice(0, ClampEnd(span, 0));
+
+ /// <summary>
+ /// Delimits all leading occurrences of whitespace charecters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ private static int ClampStart(ReadOnlySpan<char> span)
+ {
+ int start = 0;
+
+ for (; start < span.Length; start++)
+ {
+ if (!char.IsWhiteSpace(span[start]))
+ {
+ break;
+ }
+ }
+
+ return start;
+ }
+
+ /// <summary>
+ /// Delimits all trailing occurrences of whitespace charecters from the span.
+ /// </summary>
+ /// <param name="span">The source span from which the characters are removed.</param>
+ /// <param name="start">The start index from which to being searching.</param>
+ private static int ClampEnd(ReadOnlySpan<char> span, int start)
+ {
+ // Initially, start==len==0. If ClampStart trims all, start==len
+ Debug.Assert((uint)start <= span.Length);
+
+ int end = span.Length - 1;
+
+ for (; end >= start; end--)
+ {
+ if (!char.IsWhiteSpace(span[end]))
+ {
+ break;
+ }
+ }
+
+ return end - start + 1;
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/MemoryExtensions.cs b/src/System.Private.CoreLib/shared/System/MemoryExtensions.cs
index 869123a81d..4f7cab10b6 100644
--- a/src/System.Private.CoreLib/shared/System/MemoryExtensions.cs
+++ b/src/System.Private.CoreLib/shared/System/MemoryExtensions.cs
@@ -23,178 +23,6 @@ namespace System
public static partial class MemoryExtensions
{
/// <summary>
- /// Removes all leading and trailing white-space characters from the span.
- /// </summary>
- public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> span)
- {
- int start = 0;
- for (; start < span.Length; start++)
- {
- if (!char.IsWhiteSpace(span[start]))
- break;
- }
- int end = span.Length - 1;
- for (; end >= start; end--)
- {
- if (!char.IsWhiteSpace(span[end]))
- break;
- }
- return span.Slice(start, end - start + 1);
- }
-
- /// <summary>
- /// Removes all leading white-space characters from the span.
- /// </summary>
- public static ReadOnlySpan<char> TrimStart(this ReadOnlySpan<char> span)
- {
- int start = 0;
- for (; start < span.Length; start++)
- {
- if (!char.IsWhiteSpace(span[start]))
- break;
- }
- return span.Slice(start);
- }
-
- /// <summary>
- /// Removes all trailing white-space characters from the span.
- /// </summary>
- public static ReadOnlySpan<char> TrimEnd(this ReadOnlySpan<char> span)
- {
- int end = span.Length - 1;
- for (; end >= 0; end--)
- {
- if (!char.IsWhiteSpace(span[end]))
- break;
- }
- return span.Slice(0, end + 1);
- }
-
- /// <summary>
- /// Removes all leading and trailing occurrences of a specified character.
- /// </summary>
- /// <param name="span">The source span from which the character is removed.</param>
- /// <param name="trimChar">The specified character to look for and remove.</param>
- public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> span, char trimChar)
- {
- int start = 0;
- for (; start < span.Length; start++)
- {
- if (span[start] != trimChar)
- break;
- }
- int end = span.Length - 1;
- for (; end >= start; end--)
- {
- if (span[end] != trimChar)
- break;
- }
- return span.Slice(start, end - start + 1);
- }
-
- /// <summary>
- /// Removes all leading occurrences of a specified character.
- /// </summary>
- /// <param name="span">The source span from which the character is removed.</param>
- /// <param name="trimChar">The specified character to look for and remove.</param>
- public static ReadOnlySpan<char> TrimStart(this ReadOnlySpan<char> span, char trimChar)
- {
- int start = 0;
- for (; start < span.Length; start++)
- {
- if (span[start] != trimChar)
- break;
- }
- return span.Slice(start);
- }
-
- /// <summary>
- /// Removes all trailing occurrences of a specified character.
- /// </summary>
- /// <param name="span">The source span from which the character is removed.</param>
- /// <param name="trimChar">The specified character to look for and remove.</param>
- public static ReadOnlySpan<char> TrimEnd(this ReadOnlySpan<char> span, char trimChar)
- {
- int end = span.Length - 1;
- for (; end >= 0; end--)
- {
- if (span[end] != trimChar)
- break;
- }
- return span.Slice(0, end + 1);
- }
-
- /// <summary>
- /// Removes all leading and trailing occurrences of a set of characters specified
- /// in a readonly span from the span.
- /// </summary>
- /// <param name="span">The source span from which the characters are removed.</param>
- /// <param name="trimChars">The span which contains the set of characters to remove.</param>
- /// <remarks>If <paramref name="trimChars"/> is empty, white-space characters are removed instead.</remarks>
- public static ReadOnlySpan<char> Trim(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars)
- {
- return span.TrimStart(trimChars).TrimEnd(trimChars);
- }
-
- /// <summary>
- /// Removes all leading occurrences of a set of characters specified
- /// in a readonly span from the span.
- /// </summary>
- /// <param name="span">The source span from which the characters are removed.</param>
- /// <param name="trimChars">The span which contains the set of characters to remove.</param>
- /// <remarks>If <paramref name="trimChars"/> is empty, white-space characters are removed instead.</remarks>
- public static ReadOnlySpan<char> TrimStart(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars)
- {
- if (trimChars.IsEmpty)
- {
- return span.TrimStart();
- }
-
- int start = 0;
- for (; start < span.Length; start++)
- {
- for (int i = 0; i < trimChars.Length; i++)
- {
- if (span[start] == trimChars[i])
- goto Next;
- }
- break;
- Next:
- ;
- }
- return span.Slice(start);
- }
-
- /// <summary>
- /// Removes all trailing occurrences of a set of characters specified
- /// in a readonly span from the span.
- /// </summary>
- /// <param name="span">The source span from which the characters are removed.</param>
- /// <param name="trimChars">The span which contains the set of characters to remove.</param>
- /// <remarks>If <paramref name="trimChars"/> is empty, white-space characters are removed instead.</remarks>
- public static ReadOnlySpan<char> TrimEnd(this ReadOnlySpan<char> span, ReadOnlySpan<char> trimChars)
- {
- if (trimChars.IsEmpty)
- {
- return span.TrimEnd();
- }
-
- int end = span.Length - 1;
- for (; end >= 0; end--)
- {
- for (int i = 0; i < trimChars.Length; i++)
- {
- if (span[end] == trimChars[i])
- goto Next;
- }
- break;
- Next:
- ;
- }
- return span.Slice(0, end + 1);
- }
-
- /// <summary>
/// Indicates whether the specified span contains only white-space characters.
/// </summary>
public static bool IsWhiteSpace(this ReadOnlySpan<char> span)
@@ -217,17 +45,20 @@ namespace System
public static bool Contains<T>(this Span<T> span, T value)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
- return SpanHelpers.Contains(
- ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
- Unsafe.As<T, byte>(ref value),
- span.Length);
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ if (Unsafe.SizeOf<T>() == sizeof(byte))
+ return SpanHelpers.Contains(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, byte>(ref value),
+ span.Length);
- if (typeof(T) == typeof(char))
- return SpanHelpers.Contains(
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
- Unsafe.As<T, char>(ref value),
- span.Length);
+ if (Unsafe.SizeOf<T>() == sizeof(char))
+ return SpanHelpers.Contains(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, char>(ref value),
+ span.Length);
+ }
return SpanHelpers.Contains(ref MemoryMarshal.GetReference(span), value, span.Length);
}
@@ -242,17 +73,20 @@ namespace System
public static bool Contains<T>(this ReadOnlySpan<T> span, T value)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
- return SpanHelpers.Contains(
- ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
- Unsafe.As<T, byte>(ref value),
- span.Length);
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ if (Unsafe.SizeOf<T>() == sizeof(byte))
+ return SpanHelpers.Contains(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, byte>(ref value),
+ span.Length);
- if (typeof(T) == typeof(char))
- return SpanHelpers.Contains(
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
- Unsafe.As<T, char>(ref value),
- span.Length);
+ if (Unsafe.SizeOf<T>() == sizeof(char))
+ return SpanHelpers.Contains(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, char>(ref value),
+ span.Length);
+ }
return SpanHelpers.Contains(ref MemoryMarshal.GetReference(span), value, span.Length);
}
@@ -266,17 +100,20 @@ namespace System
public static int IndexOf<T>(this Span<T> span, T value)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
- return SpanHelpers.IndexOf(
- ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
- Unsafe.As<T, byte>(ref value),
- span.Length);
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ if (Unsafe.SizeOf<T>() == sizeof(byte))
+ return SpanHelpers.IndexOf(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, byte>(ref value),
+ span.Length);
- if (typeof(T) == typeof(char))
- return SpanHelpers.IndexOf(
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
- Unsafe.As<T, char>(ref value),
- span.Length);
+ if (Unsafe.SizeOf<T>() == sizeof(char))
+ return SpanHelpers.IndexOf(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, char>(ref value),
+ span.Length);
+ }
return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), value, span.Length);
}
@@ -290,18 +127,22 @@ namespace System
public static int IndexOf<T>(this Span<T> span, ReadOnlySpan<T> value)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
- return SpanHelpers.IndexOf(
- ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
- span.Length,
- ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
- value.Length);
- if (typeof(T) == typeof(char))
- return SpanHelpers.IndexOf(
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
- span.Length,
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(value)),
- value.Length);
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ if (Unsafe.SizeOf<T>() == sizeof(byte))
+ return SpanHelpers.IndexOf(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ span.Length,
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
+ value.Length);
+
+ if (Unsafe.SizeOf<T>() == sizeof(char))
+ return SpanHelpers.IndexOf(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ span.Length,
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(value)),
+ value.Length);
+ }
return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length);
}
@@ -315,17 +156,20 @@ namespace System
public static int LastIndexOf<T>(this Span<T> span, T value)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
- return SpanHelpers.LastIndexOf(
- ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
- Unsafe.As<T, byte>(ref value),
- span.Length);
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ if (Unsafe.SizeOf<T>() == sizeof(byte))
+ return SpanHelpers.LastIndexOf(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, byte>(ref value),
+ span.Length);
- if (typeof(T) == typeof(char))
- return SpanHelpers.LastIndexOf(
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
- Unsafe.As<T, char>(ref value),
- span.Length);
+ if (Unsafe.SizeOf<T>() == sizeof(char))
+ return SpanHelpers.LastIndexOf(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, char>(ref value),
+ span.Length);
+ }
return SpanHelpers.LastIndexOf<T>(ref MemoryMarshal.GetReference(span), value, span.Length);
}
@@ -339,7 +183,7 @@ namespace System
public static int LastIndexOf<T>(this Span<T> span, ReadOnlySpan<T> value)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.LastIndexOf(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
span.Length,
@@ -358,12 +202,15 @@ namespace System
{
int length = span.Length;
- if (default(T) != null && IsTypeComparableAsBytes<T>(out nuint size))
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ nuint size = (nuint)Unsafe.SizeOf<T>();
return length == other.Length &&
SpanHelpers.SequenceEqual(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(other)),
((nuint)length) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
+ }
return length == other.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(other), length);
}
@@ -374,6 +221,9 @@ namespace System
public static int SequenceCompareTo<T>(this Span<T> span, ReadOnlySpan<T> other)
where T : IComparable<T>
{
+ // Can't use IsBitwiseEquatable<T>() below because that only tells us about
+ // equality checks, not about CompareTo checks.
+
if (typeof(T) == typeof(byte))
return SpanHelpers.SequenceCompareTo(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
@@ -400,17 +250,20 @@ namespace System
public static int IndexOf<T>(this ReadOnlySpan<T> span, T value)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
- return SpanHelpers.IndexOf(
- ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
- Unsafe.As<T, byte>(ref value),
- span.Length);
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ if (Unsafe.SizeOf<T>() == sizeof(byte))
+ return SpanHelpers.IndexOf(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, byte>(ref value),
+ span.Length);
- if (typeof(T) == typeof(char))
- return SpanHelpers.IndexOf(
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
- Unsafe.As<T, char>(ref value),
- span.Length);
+ if (Unsafe.SizeOf<T>() == sizeof(char))
+ return SpanHelpers.IndexOf(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, char>(ref value),
+ span.Length);
+ }
return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), value, span.Length);
}
@@ -424,18 +277,22 @@ namespace System
public static int IndexOf<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
- return SpanHelpers.IndexOf(
- ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
- span.Length,
- ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
- value.Length);
- if (typeof(T) == typeof(char))
- return SpanHelpers.IndexOf(
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
- span.Length,
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(value)),
- value.Length);
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ if (Unsafe.SizeOf<T>() == sizeof(byte))
+ return SpanHelpers.IndexOf(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ span.Length,
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
+ value.Length);
+
+ if (Unsafe.SizeOf<T>() == sizeof(char))
+ return SpanHelpers.IndexOf(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ span.Length,
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(value)),
+ value.Length);
+ }
return SpanHelpers.IndexOf(ref MemoryMarshal.GetReference(span), span.Length, ref MemoryMarshal.GetReference(value), value.Length);
}
@@ -449,17 +306,20 @@ namespace System
public static int LastIndexOf<T>(this ReadOnlySpan<T> span, T value)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
- return SpanHelpers.LastIndexOf(
- ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
- Unsafe.As<T, byte>(ref value),
- span.Length);
-
- if (typeof(T) == typeof(char))
- return SpanHelpers.LastIndexOf(
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
- Unsafe.As<T, char>(ref value),
- span.Length);
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ if (Unsafe.SizeOf<T>() == sizeof(byte))
+ return SpanHelpers.LastIndexOf(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, byte>(ref value),
+ span.Length);
+
+ if (Unsafe.SizeOf<T>() == sizeof(char))
+ return SpanHelpers.LastIndexOf(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, char>(ref value),
+ span.Length);
+ }
return SpanHelpers.LastIndexOf<T>(ref MemoryMarshal.GetReference(span), value, span.Length);
}
@@ -473,7 +333,7 @@ namespace System
public static int LastIndexOf<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.LastIndexOf(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
span.Length,
@@ -493,18 +353,22 @@ namespace System
public static int IndexOfAny<T>(this Span<T> span, T value0, T value1)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
- return SpanHelpers.IndexOfAny(
- ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
- Unsafe.As<T, byte>(ref value0),
- Unsafe.As<T, byte>(ref value1),
- span.Length);
- if (typeof(T) == typeof(char))
- return SpanHelpers.IndexOfAny(
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
- Unsafe.As<T, char>(ref value0),
- Unsafe.As<T, char>(ref value1),
- span.Length);
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ if (Unsafe.SizeOf<T>() == sizeof(byte))
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, byte>(ref value0),
+ Unsafe.As<T, byte>(ref value1),
+ span.Length);
+
+ if (Unsafe.SizeOf<T>() == sizeof(char))
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, char>(ref value0),
+ Unsafe.As<T, char>(ref value1),
+ span.Length);
+ }
return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length);
}
@@ -520,20 +384,24 @@ namespace System
public static int IndexOfAny<T>(this Span<T> span, T value0, T value1, T value2)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
- return SpanHelpers.IndexOfAny(
- ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
- Unsafe.As<T, byte>(ref value0),
- Unsafe.As<T, byte>(ref value1),
- Unsafe.As<T, byte>(ref value2),
- span.Length);
- if (typeof(T) == typeof(char))
- return SpanHelpers.IndexOfAny(
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
- Unsafe.As<T, char>(ref value0),
- Unsafe.As<T, char>(ref value1),
- Unsafe.As<T, char>(ref value2),
- span.Length);
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ if (Unsafe.SizeOf<T>() == sizeof(byte))
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, byte>(ref value0),
+ Unsafe.As<T, byte>(ref value1),
+ Unsafe.As<T, byte>(ref value2),
+ span.Length);
+
+ if (Unsafe.SizeOf<T>() == sizeof(char))
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, char>(ref value0),
+ Unsafe.As<T, char>(ref value1),
+ Unsafe.As<T, char>(ref value2),
+ span.Length);
+ }
return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length);
}
@@ -547,87 +415,91 @@ namespace System
public static int IndexOfAny<T>(this Span<T> span, ReadOnlySpan<T> values)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
{
- ref byte valueRef = ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values));
- if (values.Length == 2)
- {
- return SpanHelpers.IndexOfAny(
- ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
- valueRef,
- Unsafe.Add(ref valueRef, 1),
- span.Length);
- }
- else if (values.Length == 3)
+ if (Unsafe.SizeOf<T>() == sizeof(byte))
{
- return SpanHelpers.IndexOfAny(
- ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
- valueRef,
- Unsafe.Add(ref valueRef, 1),
- Unsafe.Add(ref valueRef, 2),
- span.Length);
+ ref byte valueRef = ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values));
+ if (values.Length == 2)
+ {
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ valueRef,
+ Unsafe.Add(ref valueRef, 1),
+ span.Length);
+ }
+ else if (values.Length == 3)
+ {
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ valueRef,
+ Unsafe.Add(ref valueRef, 1),
+ Unsafe.Add(ref valueRef, 2),
+ span.Length);
+ }
+ else
+ {
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ span.Length,
+ ref valueRef,
+ values.Length);
+ }
}
- else
- {
- return SpanHelpers.IndexOfAny(
- ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
- span.Length,
- ref valueRef,
- values.Length);
- }
- }
- if (typeof(T) == typeof(char))
- {
- ref var valueRef = ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(values));
- if (values.Length == 5)
- {
- // Length 5 is a common length for FileSystemName expression (", <, >, *, ?) and in preference to 2 as it has an explicit overload
- return SpanHelpers.IndexOfAny(
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
- valueRef,
- Unsafe.Add(ref valueRef, 1),
- Unsafe.Add(ref valueRef, 2),
- Unsafe.Add(ref valueRef, 3),
- Unsafe.Add(ref valueRef, 4),
- span.Length);
- }
- else if (values.Length == 2)
- {
- // Length 2 is a common length for simple wildcards (*, ?), directory separators (/, \), quotes (", '), brackets, etc
- return SpanHelpers.IndexOfAny(
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
- valueRef,
- Unsafe.Add(ref valueRef, 1),
- span.Length);
- }
- else if (values.Length == 4)
- {
- // Length 4 before 3 as 3 has an explicit overload
- return SpanHelpers.IndexOfAny(
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
- valueRef,
- Unsafe.Add(ref valueRef, 1),
- Unsafe.Add(ref valueRef, 2),
- Unsafe.Add(ref valueRef, 3),
- span.Length);
- }
- else if (values.Length == 3)
- {
- return SpanHelpers.IndexOfAny(
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
- valueRef,
- Unsafe.Add(ref valueRef, 1),
- Unsafe.Add(ref valueRef, 2),
- span.Length);
- }
- else if (values.Length == 1)
+
+ if (Unsafe.SizeOf<T>() == sizeof(char))
{
- // Length 1 last, as ctoring a ReadOnlySpan to call this overload for a single value
- // is already throwing away a bunch of performance vs just calling IndexOf
- return SpanHelpers.IndexOf(
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
- valueRef,
- span.Length);
+ ref var valueRef = ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(values));
+ if (values.Length == 5)
+ {
+ // Length 5 is a common length for FileSystemName expression (", <, >, *, ?) and in preference to 2 as it has an explicit overload
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ valueRef,
+ Unsafe.Add(ref valueRef, 1),
+ Unsafe.Add(ref valueRef, 2),
+ Unsafe.Add(ref valueRef, 3),
+ Unsafe.Add(ref valueRef, 4),
+ span.Length);
+ }
+ else if (values.Length == 2)
+ {
+ // Length 2 is a common length for simple wildcards (*, ?), directory separators (/, \), quotes (", '), brackets, etc
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ valueRef,
+ Unsafe.Add(ref valueRef, 1),
+ span.Length);
+ }
+ else if (values.Length == 4)
+ {
+ // Length 4 before 3 as 3 has an explicit overload
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ valueRef,
+ Unsafe.Add(ref valueRef, 1),
+ Unsafe.Add(ref valueRef, 2),
+ Unsafe.Add(ref valueRef, 3),
+ span.Length);
+ }
+ else if (values.Length == 3)
+ {
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ valueRef,
+ Unsafe.Add(ref valueRef, 1),
+ Unsafe.Add(ref valueRef, 2),
+ span.Length);
+ }
+ else if (values.Length == 1)
+ {
+ // Length 1 last, as ctoring a ReadOnlySpan to call this overload for a single value
+ // is already throwing away a bunch of performance vs just calling IndexOf
+ return SpanHelpers.IndexOf(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ valueRef,
+ span.Length);
+ }
}
}
@@ -644,18 +516,22 @@ namespace System
public static int IndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
- return SpanHelpers.IndexOfAny(
- ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
- Unsafe.As<T, byte>(ref value0),
- Unsafe.As<T, byte>(ref value1),
- span.Length);
- if (typeof(T) == typeof(char))
- return SpanHelpers.IndexOfAny(
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
- Unsafe.As<T, char>(ref value0),
- Unsafe.As<T, char>(ref value1),
- span.Length);
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ if (Unsafe.SizeOf<T>() == sizeof(byte))
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, byte>(ref value0),
+ Unsafe.As<T, byte>(ref value1),
+ span.Length);
+
+ if (Unsafe.SizeOf<T>() == sizeof(char))
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, char>(ref value0),
+ Unsafe.As<T, char>(ref value1),
+ span.Length);
+ }
return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, span.Length);
}
@@ -671,20 +547,24 @@ namespace System
public static int IndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1, T value2)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
- return SpanHelpers.IndexOfAny(
- ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
- Unsafe.As<T, byte>(ref value0),
- Unsafe.As<T, byte>(ref value1),
- Unsafe.As<T, byte>(ref value2),
- span.Length);
- if (typeof(T) == typeof(char))
- return SpanHelpers.IndexOfAny(
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
- Unsafe.As<T, char>(ref value0),
- Unsafe.As<T, char>(ref value1),
- Unsafe.As<T, char>(ref value2),
- span.Length);
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ if (Unsafe.SizeOf<T>() == sizeof(byte))
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, byte>(ref value0),
+ Unsafe.As<T, byte>(ref value1),
+ Unsafe.As<T, byte>(ref value2),
+ span.Length);
+
+ if (Unsafe.SizeOf<T>() == sizeof(char))
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ Unsafe.As<T, char>(ref value0),
+ Unsafe.As<T, char>(ref value1),
+ Unsafe.As<T, char>(ref value2),
+ span.Length);
+ }
return SpanHelpers.IndexOfAny(ref MemoryMarshal.GetReference(span), value0, value1, value2, span.Length);
}
@@ -698,88 +578,91 @@ namespace System
public static int IndexOfAny<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> values)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
{
- ref byte valueRef = ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values));
- if (values.Length == 2)
- {
- return SpanHelpers.IndexOfAny(
- ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
- valueRef,
- Unsafe.Add(ref valueRef, 1),
- span.Length);
- }
- else if (values.Length == 3)
- {
- return SpanHelpers.IndexOfAny(
- ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
- valueRef,
- Unsafe.Add(ref valueRef, 1),
- Unsafe.Add(ref valueRef, 2),
- span.Length);
- }
- else
+ if (Unsafe.SizeOf<T>() == sizeof(byte))
{
- return SpanHelpers.IndexOfAny(
- ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
- span.Length,
- ref valueRef,
- values.Length);
+ ref byte valueRef = ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(values));
+ if (values.Length == 2)
+ {
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ valueRef,
+ Unsafe.Add(ref valueRef, 1),
+ span.Length);
+ }
+ else if (values.Length == 3)
+ {
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ valueRef,
+ Unsafe.Add(ref valueRef, 1),
+ Unsafe.Add(ref valueRef, 2),
+ span.Length);
+ }
+ else
+ {
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+ span.Length,
+ ref valueRef,
+ values.Length);
+ }
}
- }
- if (typeof(T) == typeof(char))
- {
- ref var valueRef = ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(values));
- if (values.Length == 5)
- {
- // Length 5 is a common length for FileSystemName expression (", <, >, *, ?) and in preference to 2 as it has an explicit overload
- return SpanHelpers.IndexOfAny(
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
- valueRef,
- Unsafe.Add(ref valueRef, 1),
- Unsafe.Add(ref valueRef, 2),
- Unsafe.Add(ref valueRef, 3),
- Unsafe.Add(ref valueRef, 4),
- span.Length);
- }
- else if (values.Length == 2)
+ if (Unsafe.SizeOf<T>() == sizeof(char))
{
- // Length 2 is a common length for simple wildcards (*, ?), directory separators (/, \), quotes (", '), brackets, etc
- return SpanHelpers.IndexOfAny(
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
- valueRef,
- Unsafe.Add(ref valueRef, 1),
- span.Length);
- }
- else if (values.Length == 4)
- {
- // Length 4 before 3 as 3 has an explicit overload
- return SpanHelpers.IndexOfAny(
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
- valueRef,
- Unsafe.Add(ref valueRef, 1),
- Unsafe.Add(ref valueRef, 2),
- Unsafe.Add(ref valueRef, 3),
- span.Length);
- }
- else if (values.Length == 3)
- {
- return SpanHelpers.IndexOfAny(
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
- valueRef,
- Unsafe.Add(ref valueRef, 1),
- Unsafe.Add(ref valueRef, 2),
- span.Length);
- }
- else if (values.Length == 1)
- {
- // Length 1 last, as ctoring a ReadOnlySpan to call this overload for a single value
- // is already throwing away a bunch of performance vs just calling IndexOf
- return SpanHelpers.IndexOf(
- ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
- valueRef,
- span.Length);
+ ref var valueRef = ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(values));
+ if (values.Length == 5)
+ {
+ // Length 5 is a common length for FileSystemName expression (", <, >, *, ?) and in preference to 2 as it has an explicit overload
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ valueRef,
+ Unsafe.Add(ref valueRef, 1),
+ Unsafe.Add(ref valueRef, 2),
+ Unsafe.Add(ref valueRef, 3),
+ Unsafe.Add(ref valueRef, 4),
+ span.Length);
+ }
+ else if (values.Length == 2)
+ {
+ // Length 2 is a common length for simple wildcards (*, ?), directory separators (/, \), quotes (", '), brackets, etc
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ valueRef,
+ Unsafe.Add(ref valueRef, 1),
+ span.Length);
+ }
+ else if (values.Length == 4)
+ {
+ // Length 4 before 3 as 3 has an explicit overload
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ valueRef,
+ Unsafe.Add(ref valueRef, 1),
+ Unsafe.Add(ref valueRef, 2),
+ Unsafe.Add(ref valueRef, 3),
+ span.Length);
+ }
+ else if (values.Length == 3)
+ {
+ return SpanHelpers.IndexOfAny(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ valueRef,
+ Unsafe.Add(ref valueRef, 1),
+ Unsafe.Add(ref valueRef, 2),
+ span.Length);
+ }
+ else if (values.Length == 1)
+ {
+ // Length 1 last, as ctoring a ReadOnlySpan to call this overload for a single value
+ // is already throwing away a bunch of performance vs just calling IndexOf
+ return SpanHelpers.IndexOf(
+ ref Unsafe.As<T, char>(ref MemoryMarshal.GetReference(span)),
+ valueRef,
+ span.Length);
+ }
}
}
@@ -796,7 +679,7 @@ namespace System
public static int LastIndexOfAny<T>(this Span<T> span, T value0, T value1)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, byte>(ref value0),
@@ -817,7 +700,7 @@ namespace System
public static int LastIndexOfAny<T>(this Span<T> span, T value0, T value1, T value2)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, byte>(ref value0),
@@ -837,7 +720,7 @@ namespace System
public static int LastIndexOfAny<T>(this Span<T> span, ReadOnlySpan<T> values)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
span.Length,
@@ -857,7 +740,7 @@ namespace System
public static int LastIndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, byte>(ref value0),
@@ -878,7 +761,7 @@ namespace System
public static int LastIndexOfAny<T>(this ReadOnlySpan<T> span, T value0, T value1, T value2)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
Unsafe.As<T, byte>(ref value0),
@@ -898,7 +781,7 @@ namespace System
public static int LastIndexOfAny<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> values)
where T : IEquatable<T>
{
- if (typeof(T) == typeof(byte))
+ if (Unsafe.SizeOf<T>() == sizeof(byte) && RuntimeHelpers.IsBitwiseEquatable<T>())
return SpanHelpers.LastIndexOfAny(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
span.Length,
@@ -916,12 +799,15 @@ namespace System
where T : IEquatable<T>
{
int length = span.Length;
- if (default(T) != null && IsTypeComparableAsBytes<T>(out nuint size))
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ nuint size = (nuint)Unsafe.SizeOf<T>();
return length == other.Length &&
SpanHelpers.SequenceEqual(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(other)),
((nuint)length) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
+ }
return length == other.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(other), length);
}
@@ -933,6 +819,9 @@ namespace System
public static int SequenceCompareTo<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> other)
where T : IComparable<T>
{
+ // Can't use IsBitwiseEquatable<T>() below because that only tells us about
+ // equality checks, not about CompareTo checks.
+
if (typeof(T) == typeof(byte))
return SpanHelpers.SequenceCompareTo(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
@@ -958,12 +847,15 @@ namespace System
where T : IEquatable<T>
{
int valueLength = value.Length;
- if (default(T) != null && IsTypeComparableAsBytes<T>(out nuint size))
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ nuint size = (nuint)Unsafe.SizeOf<T>();
return valueLength <= span.Length &&
SpanHelpers.SequenceEqual(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
((nuint)valueLength) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
+ }
return valueLength <= span.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(value), valueLength);
}
@@ -976,12 +868,15 @@ namespace System
where T : IEquatable<T>
{
int valueLength = value.Length;
- if (default(T) != null && IsTypeComparableAsBytes<T>(out nuint size))
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ nuint size = (nuint)Unsafe.SizeOf<T>();
return valueLength <= span.Length &&
SpanHelpers.SequenceEqual(
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
((nuint)valueLength) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
+ }
return valueLength <= span.Length && SpanHelpers.SequenceEqual(ref MemoryMarshal.GetReference(span), ref MemoryMarshal.GetReference(value), valueLength);
}
@@ -995,12 +890,15 @@ namespace System
{
int spanLength = span.Length;
int valueLength = value.Length;
- if (default(T) != null && IsTypeComparableAsBytes<T>(out nuint size))
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ nuint size = (nuint)Unsafe.SizeOf<T>();
return valueLength <= spanLength &&
SpanHelpers.SequenceEqual(
ref Unsafe.As<T, byte>(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), spanLength - valueLength)),
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
((nuint)valueLength) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
+ }
return valueLength <= spanLength &&
SpanHelpers.SequenceEqual(
@@ -1018,12 +916,15 @@ namespace System
{
int spanLength = span.Length;
int valueLength = value.Length;
- if (default(T) != null && IsTypeComparableAsBytes<T>(out nuint size))
+ if (RuntimeHelpers.IsBitwiseEquatable<T>())
+ {
+ nuint size = (nuint)Unsafe.SizeOf<T>();
return valueLength <= spanLength &&
SpanHelpers.SequenceEqual(
ref Unsafe.As<T, byte>(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), spanLength - valueLength)),
ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)),
((nuint)valueLength) * size); // If this multiplication overflows, the Span we got overflows the entire address range. There's no happy outcome for this api in such a case so we choose not to take the overhead of checking.
+ }
return valueLength <= spanLength &&
SpanHelpers.SequenceEqual(
@@ -1705,36 +1606,5 @@ namespace System
value, comparer);
return BinarySearch(span, comparable);
}
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static bool IsTypeComparableAsBytes<T>(out nuint size)
- {
- if (typeof(T) == typeof(byte) || typeof(T) == typeof(sbyte))
- {
- size = sizeof(byte);
- return true;
- }
-
- if (typeof(T) == typeof(char) || typeof(T) == typeof(short) || typeof(T) == typeof(ushort))
- {
- size = sizeof(char);
- return true;
- }
-
- if (typeof(T) == typeof(int) || typeof(T) == typeof(uint))
- {
- size = sizeof(int);
- return true;
- }
-
- if (typeof(T) == typeof(long) || typeof(T) == typeof(ulong))
- {
- size = sizeof(long);
- return true;
- }
-
- size = default;
- return false;
- }
}
}
diff --git a/src/System.Private.CoreLib/shared/System/Nullable.cs b/src/System.Private.CoreLib/shared/System/Nullable.cs
index 858f398217..6f8d667bc1 100644
--- a/src/System.Private.CoreLib/shared/System/Nullable.cs
+++ b/src/System.Private.CoreLib/shared/System/Nullable.cs
@@ -7,7 +7,7 @@ using System.Runtime.Versioning;
namespace System
{
- // Because we have special type system support that says a a boxed Nullable<T>
+ // Because we have special type system support that says a boxed Nullable<T>
// can be used where a boxed<T> is use, Nullable<T> can not implement any intefaces
// at all (since T may not). Do NOT add any interfaces to Nullable!
//
diff --git a/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs b/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs
index 6c598430ad..bf90f0449d 100644
--- a/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs
+++ b/src/System.Private.CoreLib/shared/System/ReadOnlyMemory.cs
@@ -6,6 +6,7 @@ using System.Buffers;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Text;
using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
@@ -99,7 +100,13 @@ namespace System
// No validation performed in release builds; caller must provide any necessary validation.
// 'obj is T[]' below also handles things like int[] <-> uint[] being convertible
- Debug.Assert((obj == null) || (typeof(T) == typeof(char) && obj is string) || (obj is T[]) || (obj is MemoryManager<T>));
+ Debug.Assert((obj == null)
+ || (typeof(T) == typeof(char) && obj is string)
+#if FEATURE_UTF8STRING
+ || ((typeof(T) == typeof(byte) || typeof(T) == typeof(Char8)) && obj is Utf8String)
+#endif // FEATURE_UTF8STRING
+ || (obj is T[])
+ || (obj is MemoryManager<T>));
_object = obj;
_index = start;
@@ -141,6 +148,14 @@ namespace System
{
return (_object is string str) ? str.Substring(_index, _length) : Span.ToString();
}
+#if FEATURE_UTF8STRING
+ else if (typeof(T) == typeof(Char8))
+ {
+ // TODO_UTF8STRING: Call into optimized transcoding routine when it's available.
+ ReadOnlySpan<T> span = Span;
+ return Encoding.UTF8.GetString(new ReadOnlySpan<byte>(ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)), span.Length));
+ }
+#endif // FEATURE_UTF8STRING
return string.Format("System.ReadOnlyMemory<{0}>[{1}]", typeof(T).Name, _length);
}
@@ -239,6 +254,13 @@ namespace System
refToReturn = ref Unsafe.As<char, T>(ref Unsafe.As<string>(tmpObject).GetRawStringData());
lengthOfUnderlyingSpan = Unsafe.As<string>(tmpObject).Length;
}
+#if FEATURE_UTF8STRING
+ else if ((typeof(T) == typeof(byte) || typeof(T) == typeof(Char8)) && tmpObject.GetType() == typeof(Utf8String))
+ {
+ refToReturn = ref Unsafe.As<byte, T>(ref Unsafe.As<Utf8String>(tmpObject).DangerousGetMutableReference());
+ lengthOfUnderlyingSpan = Unsafe.As<Utf8String>(tmpObject).Length;
+ }
+#endif // FEATURE_UTF8STRING
else if (RuntimeHelpers.ObjectHasComponentSize(tmpObject))
{
// We know the object is not null, it's not a string, and it is variable-length. The only
@@ -342,6 +364,14 @@ namespace System
ref char stringData = ref Unsafe.Add(ref s.GetRawStringData(), _index);
return new MemoryHandle(Unsafe.AsPointer(ref stringData), handle);
}
+#if FEATURE_UTF8STRING
+ else if ((typeof(T) == typeof(byte) || typeof(T) == typeof(Char8)) && tmpObject is Utf8String utf8String)
+ {
+ GCHandle handle = GCHandle.Alloc(tmpObject, GCHandleType.Pinned);
+ ref byte stringData = ref utf8String.DangerousGetMutableReference(_index);
+ return new MemoryHandle(Unsafe.AsPointer(ref stringData), handle);
+ }
+#endif // FEATURE_UTF8STRING
else if (RuntimeHelpers.ObjectHasComponentSize(tmpObject))
{
// 'tmpObject is T[]' below also handles things like int[] <-> uint[] being convertible
diff --git a/src/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs b/src/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs
index eb3fd1464d..00337a5fd7 100644
--- a/src/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs
+++ b/src/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs
@@ -5,6 +5,7 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
+using System.Text;
using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
using Internal.Runtime.CompilerServices;
@@ -240,12 +241,15 @@ namespace System
{
if (typeof(T) == typeof(char))
{
- unsafe
- {
- fixed (char* src = &Unsafe.As<T, char>(ref _pointer.Value))
- return new string(src, 0, _length);
- }
+ return new string(new ReadOnlySpan<char>(ref Unsafe.As<T, char>(ref _pointer.Value), _length));
}
+#if FEATURE_UTF8STRING
+ else if (typeof(T) == typeof(Char8))
+ {
+ // TODO_UTF8STRING: Call into optimized transcoding routine when it's available.
+ return Encoding.UTF8.GetString(new ReadOnlySpan<byte>(ref Unsafe.As<T, byte>(ref _pointer.Value), _length));
+ }
+#endif // FEATURE_UTF8STRING
return string.Format("System.ReadOnlySpan<{0}>[{1}]", typeof(T).Name, _length);
}
diff --git a/src/System.Private.CoreLib/shared/System/Resources/RuntimeResourceSet.cs b/src/System.Private.CoreLib/shared/System/Resources/RuntimeResourceSet.cs
index 235e47567c..210fff7bf6 100644
--- a/src/System.Private.CoreLib/shared/System/Resources/RuntimeResourceSet.cs
+++ b/src/System.Private.CoreLib/shared/System/Resources/RuntimeResourceSet.cs
@@ -148,7 +148,7 @@ namespace System.Resources
// resources, add the hash &amp; location of last byte in the name section
// to the array of resource hashes and resource name positions (carefully
// keeping these arrays sorted), add the name to the end of the name &amp;
- // offset list, possibly add the type list of types types (and increase
+ // offset list, possibly add the type list of types (and increase
// the number of items in the type table), and add the resource value at
// the end of the file. The other offsets wouldn't need to be updated to
// reflect the longer header section.
diff --git a/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs b/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs
index b1f5507122..225f434382 100644
--- a/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs
+++ b/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.cs
@@ -28,7 +28,12 @@ namespace System.Runtime.InteropServices
// As an optimization, we skip the "is string?" check below if typeof(T) is not char,
// as Memory<T> / ROM<T> can't possibly contain a string instance in this case.
- if (obj != null && (typeof(T) != typeof(char) || obj.GetType() != typeof(string)))
+ if (obj != null && !(
+ (typeof(T) == typeof(char) && obj.GetType() == typeof(string))
+#if FEATURE_UTF8STRING
+ || ((typeof(T) == typeof(byte) || typeof(T) == typeof(Char8)) && obj.GetType() == typeof(Utf8String))
+#endif // FEATURE_UTF8STRING
+ ))
{
if (RuntimeHelpers.ObjectHasComponentSize(obj))
{
diff --git a/src/System.Private.CoreLib/shared/System/Security/SecurityElement.cs b/src/System.Private.CoreLib/shared/System/Security/SecurityElement.cs
index 6c87684593..8bd335cad9 100644
--- a/src/System.Private.CoreLib/shared/System/Security/SecurityElement.cs
+++ b/src/System.Private.CoreLib/shared/System/Security/SecurityElement.cs
@@ -608,7 +608,7 @@ namespace System.Security
public SecurityElement SearchForChildByTag(string tag)
{
// Go through all the children and see if we can
- // find the one are are asked for (matching tags)
+ // find the ones that are asked for (matching tags)
if (tag == null)
throw new ArgumentNullException(nameof(tag));
diff --git a/src/System.Private.CoreLib/shared/System/Span.Fast.cs b/src/System.Private.CoreLib/shared/System/Span.Fast.cs
index 66de4fe3d3..adc1f3903d 100644
--- a/src/System.Private.CoreLib/shared/System/Span.Fast.cs
+++ b/src/System.Private.CoreLib/shared/System/Span.Fast.cs
@@ -5,6 +5,7 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
+using System.Text;
using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
using Internal.Runtime.CompilerServices;
@@ -319,12 +320,15 @@ namespace System
{
if (typeof(T) == typeof(char))
{
- unsafe
- {
- fixed (char* src = &Unsafe.As<T, char>(ref _pointer.Value))
- return new string(src, 0, _length);
- }
+ return new string(new ReadOnlySpan<char>(ref Unsafe.As<T, char>(ref _pointer.Value), _length));
+ }
+#if FEATURE_UTF8STRING
+ else if (typeof(T) == typeof(Char8))
+ {
+ // TODO_UTF8STRING: Call into optimized transcoding routine when it's available.
+ return Encoding.UTF8.GetString(new ReadOnlySpan<byte>(ref Unsafe.As<T, byte>(ref _pointer.Value), _length));
}
+#endif // FEATURE_UTF8STRING
return string.Format("System.Span<{0}>[{1}]", typeof(T).Name, _length);
}
diff --git a/src/System.Private.CoreLib/shared/System/String.cs b/src/System.Private.CoreLib/shared/System/String.cs
index 49afbc8c8c..10f75225c0 100644
--- a/src/System.Private.CoreLib/shared/System/String.cs
+++ b/src/System.Private.CoreLib/shared/System/String.cs
@@ -24,9 +24,13 @@ namespace System
[System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
public sealed partial class String : IComparable, IEnumerable, IConvertible, IEnumerable<char>, IComparable<string>, IEquatable<string>, ICloneable
{
- // String constructors
- // These are special. The implementation methods for these have a different signature from the
- // declared constructors.
+ /*
+ * CONSTRUCTORS
+ *
+ * Defining a new constructor for string-like types (like String) requires changes both
+ * to the managed code below and to the native VM code. See the comment at the top of
+ * src/vm/ecall.cpp for instructions on how to add new overloads.
+ */
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public extern String(char[] value);
@@ -335,8 +339,7 @@ namespace System
return Empty;
string result = FastAllocateString(value.Length);
- fixed (char* dest = &result._firstChar, src = &MemoryMarshal.GetReference(value))
- wstrcpy(dest, src, value.Length);
+ Buffer.Memmove(ref result._firstChar, ref MemoryMarshal.GetReference(value), (uint)value.Length);
return result;
}
diff --git a/src/System.Private.CoreLib/shared/System/Text/ASCIIUtility.cs b/src/System.Private.CoreLib/shared/System/Text/ASCIIUtility.cs
index 5bc80c35f5..755f925610 100644
--- a/src/System.Private.CoreLib/shared/System/Text/ASCIIUtility.cs
+++ b/src/System.Private.CoreLib/shared/System/Text/ASCIIUtility.cs
@@ -2,75 +2,1817 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System.Diagnostics;
+using System.Numerics;
using System.Runtime.CompilerServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+using Internal.Runtime.CompilerServices;
+
+#if BIT64
+using nint = System.Int64;
+using nuint = System.UInt64;
+#else // BIT64
+using nint = System.Int32;
+using nuint = System.UInt32;
+#endif // BIT64
namespace System.Text
{
- /*
- * Contains naive unoptimized (non-SIMD) implementations of ASCII transcoding
- * operations. Vectorized methods can be substituted here as a drop-in replacement.
- */
-
- internal unsafe static class ASCIIUtility
+ internal static partial class ASCIIUtility
{
- [MethodImpl(MethodImplOptions.NoInlining)] // the actual implementation won't be inlined, so this shouldn't be either, lest it throw off benchmarks
- public static uint GetIndexOfFirstNonAsciiByte(byte* pBytes, uint byteCount)
+ /// <summary>
+ /// Returns <see langword="true"/> iff all bytes in <paramref name="value"/> are ASCII.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool AllBytesInUInt32AreAscii(uint value)
+ {
+ return ((value & 0x80808080u) == 0);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool AllBytesInUInt64AreAscii(ulong value)
+ {
+ return ((value & 0x80808080_80808080ul) == 0);
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> iff all chars in <paramref name="value"/> are ASCII.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool AllCharsInUInt32AreAscii(uint value)
+ {
+ return ((value & ~0x007F007Fu) == 0);
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> iff all chars in <paramref name="value"/> are ASCII.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool AllCharsInUInt64AreAscii(ulong value)
+ {
+ return ((value & ~0x007F007F_007F007Ful) == 0);
+ }
+
+ /// <summary>
+ /// Given a 24-bit integer which represents a three-byte buffer read in machine endianness,
+ /// counts the number of consecutive ASCII bytes starting from the beginning of the buffer.
+ /// Returns a value 0 - 3, inclusive.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static uint CountNumberOfLeadingAsciiBytesFrom24BitInteger(uint value)
+ {
+ // This implementation seems to have better performance than tzcnt.
+
+ // The 'allBytesUpToNowAreAscii' DWORD uses bit twiddling to hold a 1 or a 0 depending
+ // on whether all processed bytes were ASCII. Then we accumulate all of the
+ // results to calculate how many consecutive ASCII bytes are present.
+
+ value = ~value;
+
+ if (BitConverter.IsLittleEndian)
+ {
+ // Read first byte
+ uint allBytesUpToNowAreAscii = (value >>= 7) & 1;
+ uint numAsciiBytes = allBytesUpToNowAreAscii;
+
+ // Read second byte
+ allBytesUpToNowAreAscii &= (value >>= 8);
+ numAsciiBytes += allBytesUpToNowAreAscii;
+
+ // Read third byte
+ allBytesUpToNowAreAscii &= (value >>= 8);
+ numAsciiBytes += allBytesUpToNowAreAscii;
+
+ return numAsciiBytes;
+ }
+ else
+ {
+ // Read first byte
+ uint allBytesUpToNowAreAscii = (value = ROL32(value, 1)) & 1;
+ uint numAsciiBytes = allBytesUpToNowAreAscii;
+
+ // Read second byte
+ allBytesUpToNowAreAscii &= (value = ROL32(value, 8));
+ numAsciiBytes += allBytesUpToNowAreAscii;
+
+ // Read third byte
+ allBytesUpToNowAreAscii &= (value = ROL32(value, 8));
+ numAsciiBytes += allBytesUpToNowAreAscii;
+
+ return numAsciiBytes;
+ }
+ }
+
+ /// <summary>
+ /// Given a DWORD which represents two packed chars in machine-endian order,
+ /// <see langword="true"/> iff the first char (in machine-endian order) is ASCII.
+ /// </summary>
+ /// <param name="value"></param>
+ /// <returns></returns>
+ private static bool FirstCharInUInt32IsAscii(uint value)
{
- uint idx = 0;
- for (; idx < byteCount; idx++)
+ return (BitConverter.IsLittleEndian && (value & 0xFF80u) == 0)
+ || (!BitConverter.IsLittleEndian && (value & 0xFF800000u) == 0);
+ }
+
+ /// <summary>
+ /// Returns the index in <paramref name="pBuffer"/> where the first non-ASCII byte is found.
+ /// Returns <paramref name="bufferLength"/> if the buffer is empty or all-ASCII.
+ /// </summary>
+ /// <returns>An ASCII byte is defined as 0x00 - 0x7F, inclusive.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe nuint GetIndexOfFirstNonAsciiByte(byte* pBuffer, nuint bufferLength)
+ {
+ // If SSE2 is supported, use those specific intrinsics instead of the generic vectorized
+ // code below. This has two benefits: (a) we can take advantage of specific instructions like
+ // pmovmskb which we know are optimized, and (b) we can avoid downclocking the processor while
+ // this method is running.
+
+ return (Sse2.IsSupported)
+ ? GetIndexOfFirstNonAsciiByte_Sse2(pBuffer, bufferLength)
+ : GetIndexOfFirstNonAsciiByte_Default(pBuffer, bufferLength);
+ }
+
+ private static unsafe nuint GetIndexOfFirstNonAsciiByte_Default(byte* pBuffer, nuint bufferLength)
+ {
+ // Squirrel away the original buffer reference. This method works by determining the exact
+ // byte reference where non-ASCII data begins, so we need this base value to perform the
+ // final subtraction at the end of the method to get the index into the original buffer.
+
+ byte* pOriginalBuffer = pBuffer;
+
+ // Before we drain off byte-by-byte, try a generic vectorized loop.
+ // Only run the loop if we have at least two vectors we can pull out.
+ // Note use of SBYTE instead of BYTE below; we're using the two's-complement
+ // representation of negative integers to act as a surrogate for "is ASCII?".
+
+ if (Vector.IsHardwareAccelerated && bufferLength >= 2 * (uint)Vector<sbyte>.Count)
{
- if ((sbyte)pBytes[idx] < 0)
+ uint SizeOfVectorInBytes = (uint)Vector<sbyte>.Count; // JIT will make this a const
+
+ if (Vector.GreaterThanOrEqualAll(Unsafe.ReadUnaligned<Vector<sbyte>>(pBuffer), Vector<sbyte>.Zero))
{
- break;
+ // The first several elements of the input buffer were ASCII. Bump up the pointer to the
+ // next aligned boundary, then perform aligned reads from here on out until we find non-ASCII
+ // data or we approach the end of the buffer. It's possible we'll reread data; this is ok.
+
+ byte* pFinalVectorReadPos = pBuffer + bufferLength - SizeOfVectorInBytes;
+ pBuffer = (byte*)(((nuint)pBuffer + SizeOfVectorInBytes) & ~(nuint)(SizeOfVectorInBytes - 1));
+
+#if DEBUG
+ long numBytesRead = pBuffer - pOriginalBuffer;
+ Debug.Assert(0 < numBytesRead && numBytesRead <= SizeOfVectorInBytes, "We should've made forward progress of at least one byte.");
+ Debug.Assert((nuint)numBytesRead <= bufferLength, "We shouldn't have read past the end of the input buffer.");
+#endif
+
+ Debug.Assert(pBuffer <= pFinalVectorReadPos, "Should be able to read at least one vector.");
+
+ do
+ {
+ Debug.Assert((nuint)pBuffer % SizeOfVectorInBytes == 0, "Vector read should be aligned.");
+ if (Vector.LessThanAny(Unsafe.Read<Vector<sbyte>>(pBuffer), Vector<sbyte>.Zero))
+ {
+ break; // found non-ASCII data
+ }
+
+ pBuffer += SizeOfVectorInBytes;
+ } while (pBuffer <= pFinalVectorReadPos);
+
+ // Adjust the remaining buffer length for the number of elements we just consumed.
+
+ bufferLength -= (nuint)pBuffer;
+ bufferLength += (nuint)pOriginalBuffer;
}
}
- return idx;
+
+ // At this point, the buffer length wasn't enough to perform a vectorized search, or we did perform
+ // a vectorized search and encountered non-ASCII data. In either case go down a non-vectorized code
+ // path to drain any remaining ASCII bytes.
+ //
+ // We're going to perform unaligned reads, so prefer 32-bit reads instead of 64-bit reads.
+ // This also allows us to perform more optimized bit twiddling tricks to count the number of ASCII bytes.
+
+ uint currentUInt32;
+
+ // Try reading 64 bits at a time in a loop.
+
+ for (; bufferLength >= 8; bufferLength -= 8)
+ {
+ currentUInt32 = Unsafe.ReadUnaligned<uint>(pBuffer);
+ uint nextUInt32 = Unsafe.ReadUnaligned<uint>(pBuffer + 4);
+
+ if (!AllBytesInUInt32AreAscii(currentUInt32 | nextUInt32))
+ {
+ // One of these two values contains non-ASCII bytes.
+ // Figure out which one it is, then put it in 'current' so that we can drain the ASCII bytes.
+
+ if (AllBytesInUInt32AreAscii(currentUInt32))
+ {
+ currentUInt32 = nextUInt32;
+ pBuffer += 4;
+ }
+
+ goto FoundNonAsciiData;
+ }
+
+ pBuffer += 8; // consumed 8 ASCII bytes
+ }
+
+ // From this point forward we don't need to update bufferLength.
+ // Try reading 32 bits.
+
+ if ((bufferLength & 4) != 0)
+ {
+ currentUInt32 = Unsafe.ReadUnaligned<uint>(pBuffer);
+ if (!AllBytesInUInt32AreAscii(currentUInt32))
+ {
+ goto FoundNonAsciiData;
+ }
+
+ pBuffer += 4;
+ }
+
+ // Try reading 16 bits.
+
+ if ((bufferLength & 2) != 0)
+ {
+ currentUInt32 = Unsafe.ReadUnaligned<ushort>(pBuffer);
+ if (!AllBytesInUInt32AreAscii(currentUInt32))
+ {
+ goto FoundNonAsciiData;
+ }
+
+ pBuffer += 2;
+ }
+
+ // Try reading 8 bits
+
+ if ((bufferLength & 1) != 0)
+ {
+ // If the buffer contains non-ASCII data, the comparison below will fail, and
+ // we'll end up not incrementing the buffer reference.
+
+ if (*(sbyte*)pBuffer >= 0)
+ {
+ pBuffer++;
+ }
+ }
+
+ Finish:
+
+ nuint totalNumBytesRead = (nuint)pBuffer - (nuint)pOriginalBuffer;
+ return totalNumBytesRead;
+
+ FoundNonAsciiData:
+
+ Debug.Assert(!AllBytesInUInt32AreAscii(currentUInt32), "Shouldn't have reached this point if we have an all-ASCII input.");
+
+ // The method being called doesn't bother looking at whether the high byte is ASCII. There are only
+ // two scenarios: (a) either one of the earlier bytes is not ASCII and the search terminates before
+ // we get to the high byte; or (b) all of the earlier bytes are ASCII, so the high byte must be
+ // non-ASCII. In both cases we only care about the low 24 bits.
+
+ pBuffer += CountNumberOfLeadingAsciiBytesFrom24BitInteger(currentUInt32);
+ goto Finish;
}
- [MethodImpl(MethodImplOptions.NoInlining)] // the actual implementation won't be inlined, so this shouldn't be either, lest it throw off benchmarks
- public static uint GetIndexOfFirstNonAsciiChar(char* pChars, uint charCount)
+ [MethodImpl(MethodImplOptions.AggressiveOptimization)]
+ private static unsafe nuint GetIndexOfFirstNonAsciiByte_Sse2(byte* pBuffer, nuint bufferLength)
{
- uint idx = 0;
- for (; idx < charCount; idx++)
+ // JIT turns the below into constants
+
+ uint SizeOfVector128 = (uint)Unsafe.SizeOf<Vector128<byte>>();
+ nuint MaskOfAllBitsInVector128 = (nuint)(SizeOfVector128 - 1);
+
+ Debug.Assert(Sse2.IsSupported, "Should've been checked by caller.");
+ Debug.Assert(BitConverter.IsLittleEndian, "SSE2 assumes little-endian.");
+
+ uint currentMask, secondMask;
+ byte* pOriginalBuffer = pBuffer;
+
+ // This method is written such that control generally flows top-to-bottom, avoiding
+ // jumps as much as possible in the optimistic case of a large enough buffer and
+ // "all ASCII". If we see non-ASCII data, we jump out of the hot paths to targets
+ // after all the main logic.
+
+ if (bufferLength < SizeOfVector128)
+ {
+ goto InputBufferLessThanOneVectorInLength; // can't vectorize; drain primitives instead
+ }
+
+ // Read the first vector unaligned.
+
+ currentMask = (uint)Sse2.MoveMask(Sse2.LoadVector128(pBuffer)); // unaligned load
+
+ if (currentMask != 0)
+ {
+ goto FoundNonAsciiDataInCurrentMask;
+ }
+
+ // If we have less than 32 bytes to process, just go straight to the final unaligned
+ // read. There's no need to mess with the loop logic in the middle of this method.
+
+ if (bufferLength < 2 * SizeOfVector128)
+ {
+ goto IncrementCurrentOffsetBeforeFinalUnalignedVectorRead;
+ }
+
+ // Now adjust the read pointer so that future reads are aligned.
+
+ pBuffer = (byte*)(((nuint)pBuffer + SizeOfVector128) & ~(nuint)MaskOfAllBitsInVector128);
+
+#if DEBUG
+ long numBytesRead = pBuffer - pOriginalBuffer;
+ Debug.Assert(0 < numBytesRead && numBytesRead <= SizeOfVector128, "We should've made forward progress of at least one byte.");
+ Debug.Assert((nuint)numBytesRead <= bufferLength, "We shouldn't have read past the end of the input buffer.");
+#endif
+
+ // Adjust the remaining length to account for what we just read.
+
+ bufferLength += (nuint)pOriginalBuffer;
+ bufferLength -= (nuint)pBuffer;
+
+ // The buffer is now properly aligned.
+ // Read 2 vectors at a time if possible.
+
+ if (bufferLength >= 2 * SizeOfVector128)
+ {
+ byte* pFinalVectorReadPos = (byte*)((nuint)pBuffer + bufferLength - 2 * SizeOfVector128);
+
+ // After this point, we no longer need to update the bufferLength value.
+
+ do
+ {
+ Vector128<byte> firstVector = Sse2.LoadAlignedVector128(pBuffer);
+ Vector128<byte> secondVector = Sse2.LoadAlignedVector128(pBuffer + SizeOfVector128);
+
+ currentMask = (uint)Sse2.MoveMask(firstVector);
+ secondMask = (uint)Sse2.MoveMask(secondVector);
+
+ if ((currentMask | secondMask) != 0)
+ {
+ goto FoundNonAsciiDataInInnerLoop;
+ }
+
+ pBuffer += 2 * SizeOfVector128;
+ } while (pBuffer <= pFinalVectorReadPos);
+ }
+
+ // We have somewhere between 0 and (2 * vector length) - 1 bytes remaining to read from.
+ // Since the above loop doesn't update bufferLength, we can't rely on its absolute value.
+ // But we _can_ rely on it to tell us how much remaining data must be drained by looking
+ // at what bits of it are set. This works because had we updated it within the loop above,
+ // we would've been adding 2 * SizeOfVector128 on each iteration, but we only care about
+ // bits which are less significant than those that the addition would've acted on.
+
+ // If there is fewer than one vector length remaining, skip the next aligned read.
+
+ if ((bufferLength & SizeOfVector128) == 0)
+ {
+ goto DoFinalUnalignedVectorRead;
+ }
+
+ // At least one full vector's worth of data remains, so we can safely read it.
+ // Remember, at this point pBuffer is still aligned.
+
+ currentMask = (uint)Sse2.MoveMask(Sse2.LoadAlignedVector128(pBuffer));
+ if (currentMask != 0)
{
- if (pChars[idx] > 0x7Fu)
+ goto FoundNonAsciiDataInCurrentMask;
+ }
+
+ IncrementCurrentOffsetBeforeFinalUnalignedVectorRead:
+
+ pBuffer += SizeOfVector128;
+
+ DoFinalUnalignedVectorRead:
+
+ if (((byte)bufferLength & MaskOfAllBitsInVector128) != 0)
+ {
+ // Perform an unaligned read of the last vector.
+ // We need to adjust the pointer because we're re-reading data.
+
+ pBuffer += (bufferLength & MaskOfAllBitsInVector128) - SizeOfVector128;
+
+ currentMask = (uint)Sse2.MoveMask(Sse2.LoadVector128(pBuffer)); // unaligned load
+ if (currentMask != 0)
{
- break;
+ goto FoundNonAsciiDataInCurrentMask;
}
+
+ pBuffer += SizeOfVector128;
}
- return idx;
+
+ Finish:
+
+ return (nuint)pBuffer - (nuint)pOriginalBuffer; // and we're done!
+
+ FoundNonAsciiDataInInnerLoop:
+
+ // If the current (first) mask isn't the mask that contains non-ASCII data, then it must
+ // instead be the second mask. If so, skip the entire first mask and drain ASCII bytes
+ // from the second mask.
+
+ if (currentMask == 0)
+ {
+ pBuffer += SizeOfVector128;
+ currentMask = secondMask;
+ }
+
+ FoundNonAsciiDataInCurrentMask:
+
+ // The mask contains - from the LSB - a 0 for each ASCII byte we saw, and a 1 for each non-ASCII byte.
+ // Tzcnt is the correct operation to count the number of zero bits quickly. If this instruction isn't
+ // available, we'll fall back to a normal loop.
+
+ Debug.Assert(currentMask != 0, "Shouldn't be here unless we see non-ASCII data.");
+ pBuffer += (uint)BitOperations.TrailingZeroCount(currentMask);
+
+ goto Finish;
+
+ FoundNonAsciiDataInCurrentDWord:
+
+ uint currentDWord;
+ Debug.Assert(!AllBytesInUInt32AreAscii(currentDWord), "Shouldn't be here unless we see non-ASCII data.");
+ pBuffer += CountNumberOfLeadingAsciiBytesFrom24BitInteger(currentDWord);
+
+ goto Finish;
+
+ InputBufferLessThanOneVectorInLength:
+
+ // These code paths get hit if the original input length was less than one vector in size.
+ // We can't perform vectorized reads at this point, so we'll fall back to reading primitives
+ // directly. Note that all of these reads are unaligned.
+
+ Debug.Assert(bufferLength < SizeOfVector128);
+
+ // QWORD drain
+
+ if ((bufferLength & 8) != 0)
+ {
+ if (Bmi1.X64.IsSupported)
+ {
+ // If we can use 64-bit tzcnt to count the number of leading ASCII bytes, prefer it.
+
+ ulong candidateUInt64 = Unsafe.ReadUnaligned<ulong>(pBuffer);
+ if (!AllBytesInUInt64AreAscii(candidateUInt64))
+ {
+ // Clear everything but the high bit of each byte, then tzcnt.
+ // Remember the / 8 at the end to convert bit count to byte count.
+
+ candidateUInt64 &= 0x80808080_80808080ul;
+ pBuffer += (nuint)(Bmi1.X64.TrailingZeroCount(candidateUInt64) / 8);
+ goto Finish;
+ }
+ }
+ else
+ {
+ // If we can't use 64-bit tzcnt, no worries. We'll just do 2x 32-bit reads instead.
+
+ currentDWord = Unsafe.ReadUnaligned<uint>(pBuffer);
+ uint nextDWord = Unsafe.ReadUnaligned<uint>(pBuffer + 4);
+
+ if (!AllBytesInUInt32AreAscii(currentDWord | nextDWord))
+ {
+ // At least one of the values wasn't all-ASCII.
+ // We need to figure out which one it was and stick it in the currentMask local.
+
+ if (AllBytesInUInt32AreAscii(currentDWord))
+ {
+ currentDWord = nextDWord; // this one is the culprit
+ pBuffer += 4;
+ }
+
+ goto FoundNonAsciiDataInCurrentDWord;
+ }
+ }
+
+ pBuffer += 8; // successfully consumed 8 ASCII bytes
+ }
+
+ // DWORD drain
+
+ if ((bufferLength & 4) != 0)
+ {
+ currentDWord = Unsafe.ReadUnaligned<uint>(pBuffer);
+
+ if (!AllBytesInUInt32AreAscii(currentDWord))
+ {
+ goto FoundNonAsciiDataInCurrentDWord;
+ }
+
+ pBuffer += 4; // successfully consumed 4 ASCII bytes
+ }
+
+ // WORD drain
+ // (We movzx to a DWORD for ease of manipulation.)
+
+ if ((bufferLength & 2) != 0)
+ {
+ currentDWord = Unsafe.ReadUnaligned<ushort>(pBuffer);
+
+ if (!AllBytesInUInt32AreAscii(currentDWord))
+ {
+ // We only care about the 0x0080 bit of the value. If it's not set, then we
+ // increment currentOffset by 1. If it's set, we don't increment it at all.
+
+ pBuffer += (nuint)((nint)(sbyte)currentDWord >> 7) + 1;
+ goto Finish;
+ }
+
+ pBuffer += 2; // successfully consumed 2 ASCII bytes
+ }
+
+ // BYTE drain
+
+ if ((bufferLength & 1) != 0)
+ {
+ // sbyte has non-negative value if byte is ASCII.
+
+ if (*(sbyte*)(pBuffer) >= 0)
+ {
+ pBuffer++; // successfully consumed a single byte
+ }
+ }
+
+ goto Finish;
}
- [MethodImpl(MethodImplOptions.NoInlining)] // the actual implementation won't be inlined, so this shouldn't be either, lest it throw off benchmarks
- public static uint NarrowUtf16ToAscii(char* pChars, byte* pBytes, uint elementCount)
+ /// <summary>
+ /// Returns the index in <paramref name="pBuffer"/> where the first non-ASCII char is found.
+ /// Returns <paramref name="bufferLength"/> if the buffer is empty or all-ASCII.
+ /// </summary>
+ /// <returns>An ASCII char is defined as 0x0000 - 0x007F, inclusive.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe nuint GetIndexOfFirstNonAsciiChar(char* pBuffer, nuint bufferLength /* in chars */)
{
- uint idx = 0;
- for (; idx < elementCount; idx++)
+ // If SSE2 is supported, use those specific intrinsics instead of the generic vectorized
+ // code below. This has two benefits: (a) we can take advantage of specific instructions like
+ // pmovmskb which we know are optimized, and (b) we can avoid downclocking the processor while
+ // this method is running.
+
+ return (Sse2.IsSupported)
+ ? GetIndexOfFirstNonAsciiChar_Sse2(pBuffer, bufferLength)
+ : GetIndexOfFirstNonAsciiChar_Default(pBuffer, bufferLength);
+ }
+
+ private static unsafe nuint GetIndexOfFirstNonAsciiChar_Default(char* pBuffer, nuint bufferLength /* in chars */)
+ {
+ // Squirrel away the original buffer reference.This method works by determining the exact
+ // char reference where non-ASCII data begins, so we need this base value to perform the
+ // final subtraction at the end of the method to get the index into the original buffer.
+
+ char* pOriginalBuffer = pBuffer;
+
+ Debug.Assert(bufferLength <= nuint.MaxValue / sizeof(char));
+
+ // Before we drain off char-by-char, try a generic vectorized loop.
+ // Only run the loop if we have at least two vectors we can pull out.
+
+ if (Vector.IsHardwareAccelerated && bufferLength >= 2 * (uint)Vector<ushort>.Count)
+ {
+ uint SizeOfVectorInChars = (uint)Vector<ushort>.Count; // JIT will make this a const
+ uint SizeOfVectorInBytes = (uint)Vector<byte>.Count; // JIT will make this a const
+
+ Vector<ushort> maxAscii = new Vector<ushort>(0x007F);
+
+ if (Vector.LessThanOrEqualAll(Unsafe.ReadUnaligned<Vector<ushort>>(pBuffer), maxAscii))
+ {
+ // The first several elements of the input buffer were ASCII. Bump up the pointer to the
+ // next aligned boundary, then perform aligned reads from here on out until we find non-ASCII
+ // data or we approach the end of the buffer. It's possible we'll reread data; this is ok.
+
+ char* pFinalVectorReadPos = pBuffer + bufferLength - SizeOfVectorInChars;
+ pBuffer = (char*)(((nuint)pBuffer + SizeOfVectorInBytes) & ~(nuint)(SizeOfVectorInBytes - 1));
+
+#if DEBUG
+ long numCharsRead = pBuffer - pOriginalBuffer;
+ Debug.Assert(0 < numCharsRead && numCharsRead <= SizeOfVectorInChars, "We should've made forward progress of at least one char.");
+ Debug.Assert((nuint)numCharsRead <= bufferLength, "We shouldn't have read past the end of the input buffer.");
+#endif
+
+ Debug.Assert(pBuffer <= pFinalVectorReadPos, "Should be able to read at least one vector.");
+
+ do
+ {
+ Debug.Assert((nuint)pBuffer % SizeOfVectorInChars == 0, "Vector read should be aligned.");
+ if (Vector.GreaterThanAny(Unsafe.Read<Vector<ushort>>(pBuffer), maxAscii))
+ {
+ break; // found non-ASCII data
+ }
+ pBuffer += SizeOfVectorInChars;
+ } while (pBuffer <= pFinalVectorReadPos);
+
+ // Adjust the remaining buffer length for the number of elements we just consumed.
+
+ bufferLength -= ((nuint)pBuffer - (nuint)pOriginalBuffer) / sizeof(char);
+ }
+ }
+
+ // At this point, the buffer length wasn't enough to perform a vectorized search, or we did perform
+ // a vectorized search and encountered non-ASCII data. In either case go down a non-vectorized code
+ // path to drain any remaining ASCII chars.
+ //
+ // We're going to perform unaligned reads, so prefer 32-bit reads instead of 64-bit reads.
+ // This also allows us to perform more optimized bit twiddling tricks to count the number of ASCII chars.
+
+ uint currentUInt32;
+
+ // Try reading 64 bits at a time in a loop.
+
+ for (; bufferLength >= 4; bufferLength -= 4) // 64 bits = 4 * 16-bit chars
{
- uint ch = pChars[idx];
- if (ch > 0x7Fu)
+ currentUInt32 = Unsafe.ReadUnaligned<uint>(pBuffer);
+ uint nextUInt32 = Unsafe.ReadUnaligned<uint>(pBuffer + 4 / sizeof(char));
+
+ if (!AllCharsInUInt32AreAscii(currentUInt32 | nextUInt32))
{
- break;
+ // One of these two values contains non-ASCII chars.
+ // Figure out which one it is, then put it in 'current' so that we can drain the ASCII chars.
+
+ if (AllCharsInUInt32AreAscii(currentUInt32))
+ {
+ currentUInt32 = nextUInt32;
+ pBuffer += 2;
+ }
+
+ goto FoundNonAsciiData;
+ }
+
+ pBuffer += 4; // consumed 4 ASCII chars
+ }
+
+ // From this point forward we don't need to keep track of the remaining buffer length.
+ // Try reading 32 bits.
+
+ if ((bufferLength & 2) != 0) // 32 bits = 2 * 16-bit chars
+ {
+ currentUInt32 = Unsafe.ReadUnaligned<uint>(pBuffer);
+ if (!AllCharsInUInt32AreAscii(currentUInt32))
+ {
+ goto FoundNonAsciiData;
}
- pBytes[idx] = (byte)ch;
+
+ pBuffer += 2;
}
- return idx;
+
+ // Try reading 16 bits.
+ // No need to try an 8-bit read after this since we're working with chars.
+
+ if ((bufferLength & 1) != 0)
+ {
+ // If the buffer contains non-ASCII data, the comparison below will fail, and
+ // we'll end up not incrementing the buffer reference.
+
+ if (*pBuffer <= 0x007F)
+ {
+ pBuffer++;
+ }
+ }
+
+ Finish:
+
+ nuint totalNumBytesRead = (nuint)pBuffer - (nuint)pOriginalBuffer;
+ Debug.Assert(totalNumBytesRead % sizeof(char) == 0, "Total number of bytes read should be even since we're working with chars.");
+ return totalNumBytesRead / sizeof(char); // convert byte count -> char count before returning
+
+ FoundNonAsciiData:
+
+ Debug.Assert(!AllCharsInUInt32AreAscii(currentUInt32), "Shouldn't have reached this point if we have an all-ASCII input.");
+
+ // We don't bother looking at the second char - only the first char.
+
+ if (FirstCharInUInt32IsAscii(currentUInt32))
+ {
+ pBuffer++;
+ }
+
+ goto Finish;
}
- [MethodImpl(MethodImplOptions.NoInlining)] // the actual implementation won't be inlined, so this shouldn't be either, lest it throw off benchmarks
- public static uint WidenAsciiToUtf16(byte* pBytes, char* pChars, uint elementCount)
+ [MethodImpl(MethodImplOptions.AggressiveOptimization)]
+ private static unsafe nuint GetIndexOfFirstNonAsciiChar_Sse2(char* pBuffer, nuint bufferLength /* in chars */)
{
- uint idx = 0;
- for (; idx < elementCount; idx++)
+ // This method contains logic optimized for both SSE2 and SSE41. Much of the logic in this method
+ // will be elided by JIT once we determine which specific ISAs we support.
+
+ // Quick check for empty inputs.
+
+ if (bufferLength == 0)
+ {
+ return 0;
+ }
+
+ // JIT turns the below into constants
+
+ uint SizeOfVector128InBytes = (uint)Unsafe.SizeOf<Vector128<byte>>();
+ uint SizeOfVector128InChars = SizeOfVector128InBytes / sizeof(char);
+
+ Debug.Assert(Sse2.IsSupported, "Should've been checked by caller.");
+ Debug.Assert(BitConverter.IsLittleEndian, "SSE2 assumes little-endian.");
+
+ Vector128<short> firstVector, secondVector;
+ uint currentMask;
+ char* pOriginalBuffer = pBuffer;
+
+ if (bufferLength < SizeOfVector128InChars)
+ {
+ goto InputBufferLessThanOneVectorInLength; // can't vectorize; drain primitives instead
+ }
+
+ // This method is written such that control generally flows top-to-bottom, avoiding
+ // jumps as much as possible in the optimistic case of "all ASCII". If we see non-ASCII
+ // data, we jump out of the hot paths to targets at the end of the method.
+
+ Vector128<short> asciiMaskForPTEST = Vector128.Create(unchecked((short)0xFF80)); // used for PTEST on supported hardware
+ Vector128<ushort> asciiMaskForPMINUW = Vector128.Create((ushort)0x0080); // used for PMINUW on supported hardware
+ Vector128<short> asciiMaskForPXOR = Vector128.Create(unchecked((short)0x8000)); // used for PXOR
+ Vector128<short> asciiMaskForPCMPGTW = Vector128.Create(unchecked((short)0x807F)); // used for PCMPGTW
+
+ Debug.Assert(bufferLength <= nuint.MaxValue / sizeof(char));
+
+ // Read the first vector unaligned.
+
+ firstVector = Sse2.LoadVector128((short*)pBuffer); // unaligned load
+
+ if (Sse41.IsSupported)
+ {
+ // The SSE41-optimized code path works by forcing the 0x0080 bit in each WORD of the vector to be
+ // set iff the WORD element has value >= 0x0080 (non-ASCII). Then we'll treat it as a BYTE vector
+ // in order to extract the mask.
+ currentMask = (uint)Sse2.MoveMask(Sse41.Min(firstVector.AsUInt16(), asciiMaskForPMINUW).AsByte());
+ }
+ else
+ {
+ // The SSE2-optimized code path works by forcing each WORD of the vector to be 0xFFFF iff the WORD
+ // element has value >= 0x0080 (non-ASCII). Then we'll treat it as a BYTE vector in order to extract
+ // the mask.
+ currentMask = (uint)Sse2.MoveMask(Sse2.CompareGreaterThan(Sse2.Xor(firstVector, asciiMaskForPXOR), asciiMaskForPCMPGTW).AsByte());
+ }
+
+ if (currentMask != 0)
+ {
+ goto FoundNonAsciiDataInCurrentMask;
+ }
+
+ // If we have less than 32 bytes to process, just go straight to the final unaligned
+ // read. There's no need to mess with the loop logic in the middle of this method.
+
+ // Adjust the remaining length to account for what we just read.
+ // For the remainder of this code path, bufferLength will be in bytes, not chars.
+
+ bufferLength <<= 1; // chars to bytes
+
+ if (bufferLength < 2 * SizeOfVector128InBytes)
+ {
+ goto IncrementCurrentOffsetBeforeFinalUnalignedVectorRead;
+ }
+
+ // Now adjust the read pointer so that future reads are aligned.
+
+ pBuffer = (char*)(((nuint)pBuffer + SizeOfVector128InBytes) & ~(nuint)(SizeOfVector128InBytes - 1));
+
+#if DEBUG
+ long numCharsRead = pBuffer - pOriginalBuffer;
+ Debug.Assert(0 < numCharsRead && numCharsRead <= SizeOfVector128InChars, "We should've made forward progress of at least one char.");
+ Debug.Assert((nuint)numCharsRead <= bufferLength, "We shouldn't have read past the end of the input buffer.");
+#endif
+
+ // Adjust remaining buffer length.
+
+ bufferLength += (nuint)pOriginalBuffer;
+ bufferLength -= (nuint)pBuffer;
+
+ // The buffer is now properly aligned.
+ // Read 2 vectors at a time if possible.
+
+ if (bufferLength >= 2 * SizeOfVector128InBytes)
+ {
+ char* pFinalVectorReadPos = (char*)((nuint)pBuffer + bufferLength - 2 * SizeOfVector128InBytes);
+
+ // After this point, we no longer need to update the bufferLength value.
+
+ do
+ {
+ firstVector = Sse2.LoadAlignedVector128((short*)pBuffer);
+ secondVector = Sse2.LoadAlignedVector128((short*)pBuffer + SizeOfVector128InChars);
+ Vector128<short> combinedVector = Sse2.Or(firstVector, secondVector);
+
+ if (Sse41.IsSupported)
+ {
+ // If a non-ASCII bit is set in any WORD of the combined vector, we have seen non-ASCII data.
+ // Jump to the non-ASCII handler to figure out which particular vector contained non-ASCII data.
+ if (!Sse41.TestZ(combinedVector, asciiMaskForPTEST))
+ {
+ goto FoundNonAsciiDataInFirstOrSecondVector;
+ }
+ }
+ else
+ {
+ // See comment earlier in the method for an explanation of how the below logic works.
+ if (Sse2.MoveMask(Sse2.CompareGreaterThan(Sse2.Xor(combinedVector, asciiMaskForPXOR), asciiMaskForPCMPGTW).AsByte()) != 0)
+ {
+ goto FoundNonAsciiDataInFirstOrSecondVector;
+ }
+ }
+
+ pBuffer += 2 * SizeOfVector128InChars;
+ } while (pBuffer <= pFinalVectorReadPos);
+ }
+
+ // We have somewhere between 0 and (2 * vector length) - 1 bytes remaining to read from.
+ // Since the above loop doesn't update bufferLength, we can't rely on its absolute value.
+ // But we _can_ rely on it to tell us how much remaining data must be drained by looking
+ // at what bits of it are set. This works because had we updated it within the loop above,
+ // we would've been adding 2 * SizeOfVector128 on each iteration, but we only care about
+ // bits which are less significant than those that the addition would've acted on.
+
+ // If there is fewer than one vector length remaining, skip the next aligned read.
+ // Remember, at this point bufferLength is measured in bytes, not chars.
+
+ if ((bufferLength & SizeOfVector128InBytes) == 0)
+ {
+ goto DoFinalUnalignedVectorRead;
+ }
+
+ // At least one full vector's worth of data remains, so we can safely read it.
+ // Remember, at this point pBuffer is still aligned.
+
+ firstVector = Sse2.LoadAlignedVector128((short*)pBuffer);
+
+ if (Sse41.IsSupported)
{
- byte b = pBytes[idx];
- if (b > 0x7F)
+ // If a non-ASCII bit is set in any WORD of the combined vector, we have seen non-ASCII data.
+ // Jump to the non-ASCII handler to figure out which particular vector contained non-ASCII data.
+ if (!Sse41.TestZ(firstVector, asciiMaskForPTEST))
+ {
+ goto FoundNonAsciiDataInFirstVector;
+ }
+ }
+ else
+ {
+ // See comment earlier in the method for an explanation of how the below logic works.
+ currentMask = (uint)Sse2.MoveMask(Sse2.CompareGreaterThan(Sse2.Xor(firstVector, asciiMaskForPXOR), asciiMaskForPCMPGTW).AsByte());
+ if (currentMask != 0)
+ {
+ goto FoundNonAsciiDataInCurrentMask;
+ }
+ }
+
+ IncrementCurrentOffsetBeforeFinalUnalignedVectorRead:
+
+ pBuffer += SizeOfVector128InChars;
+
+ DoFinalUnalignedVectorRead:
+
+ if (((byte)bufferLength & (SizeOfVector128InBytes - 1)) != 0)
+ {
+ // Perform an unaligned read of the last vector.
+ // We need to adjust the pointer because we're re-reading data.
+
+ pBuffer = (char*)((byte*)pBuffer + (bufferLength & (SizeOfVector128InBytes - 1)) - SizeOfVector128InBytes);
+ firstVector = Sse2.LoadVector128((short*)pBuffer); // unaligned load
+
+ if (Sse41.IsSupported)
+ {
+ // If a non-ASCII bit is set in any WORD of the combined vector, we have seen non-ASCII data.
+ // Jump to the non-ASCII handler to figure out which particular vector contained non-ASCII data.
+ if (!Sse41.TestZ(firstVector, asciiMaskForPTEST))
+ {
+ goto FoundNonAsciiDataInFirstVector;
+ }
+ }
+ else
+ {
+ // See comment earlier in the method for an explanation of how the below logic works.
+ currentMask = (uint)Sse2.MoveMask(Sse2.CompareGreaterThan(Sse2.Xor(firstVector, asciiMaskForPXOR), asciiMaskForPCMPGTW).AsByte());
+ if (currentMask != 0)
+ {
+ goto FoundNonAsciiDataInCurrentMask;
+ }
+ }
+
+ pBuffer += SizeOfVector128InChars;
+ }
+
+ Finish:
+
+ Debug.Assert(((nuint)pBuffer - (nuint)pOriginalBuffer) % 2 == 0, "Shouldn't have incremented any pointer by an odd byte count.");
+ return ((nuint)pBuffer - (nuint)pOriginalBuffer) / sizeof(char); // and we're done! (remember to adjust for char count)
+
+ FoundNonAsciiDataInFirstOrSecondVector:
+
+ // We don't know if the first or the second vector contains non-ASCII data. Check the first
+ // vector, and if that's all-ASCII then the second vector must be the culprit. Either way
+ // we'll make sure the first vector local is the one that contains the non-ASCII data.
+
+ // See comment earlier in the method for an explanation of how the below logic works.
+ if (Sse41.IsSupported)
+ {
+ if (!Sse41.TestZ(firstVector, asciiMaskForPTEST))
+ {
+ goto FoundNonAsciiDataInFirstVector;
+ }
+ }
+ else
+ {
+ currentMask = (uint)Sse2.MoveMask(Sse2.CompareGreaterThan(Sse2.Xor(firstVector, asciiMaskForPXOR), asciiMaskForPCMPGTW).AsByte());
+ if (currentMask != 0)
+ {
+ goto FoundNonAsciiDataInCurrentMask;
+ }
+ }
+
+ // Wasn't the first vector; must be the second.
+
+ pBuffer += SizeOfVector128InChars;
+ firstVector = secondVector;
+
+ FoundNonAsciiDataInFirstVector:
+
+ // See comment earlier in the method for an explanation of how the below logic works.
+ if (Sse41.IsSupported)
+ {
+ currentMask = (uint)Sse2.MoveMask(Sse41.Min(firstVector.AsUInt16(), asciiMaskForPMINUW).AsByte());
+ }
+ else
+ {
+ currentMask = (uint)Sse2.MoveMask(Sse2.CompareGreaterThan(Sse2.Xor(firstVector, asciiMaskForPXOR), asciiMaskForPCMPGTW).AsByte());
+ }
+
+ FoundNonAsciiDataInCurrentMask:
+
+ // The mask contains - from the LSB - a 0 for each ASCII byte we saw, and a 1 for each non-ASCII byte.
+ // Tzcnt is the correct operation to count the number of zero bits quickly. If this instruction isn't
+ // available, we'll fall back to a normal loop. (Even though the original vector used WORD elements,
+ // masks work on BYTE elements, and we account for this in the final fixup.)
+
+ Debug.Assert(currentMask != 0, "Shouldn't be here unless we see non-ASCII data.");
+ pBuffer = (char*)((byte*)pBuffer + (uint)BitOperations.TrailingZeroCount(currentMask));
+
+ goto Finish;
+
+ FoundNonAsciiDataInCurrentDWord:
+
+ uint currentDWord;
+ Debug.Assert(!AllCharsInUInt32AreAscii(currentDWord), "Shouldn't be here unless we see non-ASCII data.");
+
+ if (FirstCharInUInt32IsAscii(currentDWord))
+ {
+ pBuffer++; // skip past the ASCII char
+ }
+
+ goto Finish;
+
+ InputBufferLessThanOneVectorInLength:
+
+ // These code paths get hit if the original input length was less than one vector in size.
+ // We can't perform vectorized reads at this point, so we'll fall back to reading primitives
+ // directly. Note that all of these reads are unaligned.
+
+ // Reminder: If this code path is hit, bufferLength is still a char count, not a byte count.
+ // We skipped the code path that multiplied the count by sizeof(char).
+
+ Debug.Assert(bufferLength < SizeOfVector128InChars);
+
+ // QWORD drain
+
+ if ((bufferLength & 4) != 0)
+ {
+ if (Bmi1.X64.IsSupported)
+ {
+ // If we can use 64-bit tzcnt to count the number of leading ASCII chars, prefer it.
+
+ ulong candidateUInt64 = Unsafe.ReadUnaligned<ulong>(pBuffer);
+ if (!AllCharsInUInt64AreAscii(candidateUInt64))
+ {
+ // Clear the low 7 bits (the ASCII bits) of each char, then tzcnt.
+ // Remember the / 8 at the end to convert bit count to byte count,
+ // then the & ~1 at the end to treat a match in the high byte of
+ // any char the same as a match in the low byte of that same char.
+
+ candidateUInt64 &= 0xFF80FF80_FF80FF80ul;
+ pBuffer = (char*)((byte*)pBuffer + ((nuint)(Bmi1.X64.TrailingZeroCount(candidateUInt64) / 8) & ~(nuint)1));
+ goto Finish;
+ }
+ }
+ else
+ {
+ // If we can't use 64-bit tzcnt, no worries. We'll just do 2x 32-bit reads instead.
+
+ currentDWord = Unsafe.ReadUnaligned<uint>(pBuffer);
+ uint nextDWord = Unsafe.ReadUnaligned<uint>(pBuffer + 4 / sizeof(char));
+
+ if (!AllCharsInUInt32AreAscii(currentDWord | nextDWord))
+ {
+ // At least one of the values wasn't all-ASCII.
+ // We need to figure out which one it was and stick it in the currentMask local.
+
+ if (AllCharsInUInt32AreAscii(currentDWord))
+ {
+ currentDWord = nextDWord; // this one is the culprit
+ pBuffer += 4 / sizeof(char);
+ }
+
+ goto FoundNonAsciiDataInCurrentDWord;
+ }
+ }
+
+ pBuffer += 4; // successfully consumed 4 ASCII chars
+ }
+
+ // DWORD drain
+
+ if ((bufferLength & 2) != 0)
+ {
+ currentDWord = Unsafe.ReadUnaligned<uint>(pBuffer);
+
+ if (!AllCharsInUInt32AreAscii(currentDWord))
+ {
+ goto FoundNonAsciiDataInCurrentDWord;
+ }
+
+ pBuffer += 2; // successfully consumed 2 ASCII chars
+ }
+
+ // WORD drain
+ // This is the final drain; there's no need for a BYTE drain since our elemental type is 16-bit char.
+
+ if ((bufferLength & 1) != 0)
+ {
+ if (*pBuffer <= 0x007F)
+ {
+ pBuffer++; // successfully consumed a single char
+ }
+ }
+
+ goto Finish;
+ }
+
+ /// <summary>
+ /// Given a QWORD which represents a buffer of 4 ASCII chars in machine-endian order,
+ /// narrows each WORD to a BYTE, then writes the 4-byte result to the output buffer
+ /// also in machine-endian order.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ private static void NarrowFourUtf16CharsToAsciiAndWriteToBuffer(ref byte outputBuffer, ulong value)
+ {
+ Debug.Assert(AllCharsInUInt64AreAscii(value));
+
+ if (Bmi2.X64.IsSupported)
+ {
+ // BMI2 will work regardless of the processor's endianness.
+ Unsafe.WriteUnaligned(ref outputBuffer, (uint)Bmi2.X64.ParallelBitExtract(value, 0x00FF00FF_00FF00FFul));
+ }
+ else
+ {
+ if (BitConverter.IsLittleEndian)
+ {
+ outputBuffer = (byte)value;
+ value >>= 16;
+ Unsafe.Add(ref outputBuffer, 1) = (byte)value;
+ value >>= 16;
+ Unsafe.Add(ref outputBuffer, 2) = (byte)value;
+ value >>= 16;
+ Unsafe.Add(ref outputBuffer, 3) = (byte)value;
+ }
+ else
+ {
+ Unsafe.Add(ref outputBuffer, 3) = (byte)value;
+ value >>= 16;
+ Unsafe.Add(ref outputBuffer, 2) = (byte)value;
+ value >>= 16;
+ Unsafe.Add(ref outputBuffer, 1) = (byte)value;
+ value >>= 16;
+ outputBuffer = (byte)value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Given a DWORD which represents a buffer of 2 ASCII chars in machine-endian order,
+ /// narrows each WORD to a BYTE, then writes the 2-byte result to the output buffer also in
+ /// machine-endian order.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ private static void NarrowTwoUtf16CharsToAsciiAndWriteToBuffer(ref byte outputBuffer, uint value)
+ {
+ Debug.Assert(AllCharsInUInt32AreAscii(value));
+
+ if (BitConverter.IsLittleEndian)
+ {
+ outputBuffer = (byte)value;
+ Unsafe.Add(ref outputBuffer, 1) = (byte)(value >> 16);
+ }
+ else
+ {
+ Unsafe.Add(ref outputBuffer, 1) = (byte)value;
+ outputBuffer = (byte)(value >> 16);
+ }
+ }
+
+ /// <summary>
+ /// Copies as many ASCII characters (U+0000..U+007F) as possible from <paramref name="pUtf16Buffer"/>
+ /// to <paramref name="pAsciiBuffer"/>, stopping when the first non-ASCII character is encountered
+ /// or once <paramref name="elementCount"/> elements have been converted. Returns the total number
+ /// of elements that were able to be converted.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveOptimization)]
+ public static unsafe nuint NarrowUtf16ToAscii(char* pUtf16Buffer, byte* pAsciiBuffer, nuint elementCount)
+ {
+ nuint currentOffset = 0;
+
+ uint utf16Data32BitsHigh = 0, utf16Data32BitsLow = 0;
+ ulong utf16Data64Bits = 0;
+
+ // If SSE2 is supported, use those specific intrinsics instead of the generic vectorized
+ // code below. This has two benefits: (a) we can take advantage of specific instructions like
+ // pmovmskb, ptest, vpminuw which we know are optimized, and (b) we can avoid downclocking the
+ // processor while this method is running.
+
+ if (Sse2.IsSupported)
+ {
+ Debug.Assert(BitConverter.IsLittleEndian, "Assume little endian if SSE2 is supported.");
+
+ if (elementCount >= 2 * (uint)Unsafe.SizeOf<Vector128<byte>>())
+ {
+ // Since there's overhead to setting up the vectorized code path, we only want to
+ // call into it after a quick probe to ensure the next immediate characters really are ASCII.
+ // If we see non-ASCII data, we'll jump immediately to the draining logic at the end of the method.
+
+ if (IntPtr.Size >= 8)
+ {
+ utf16Data64Bits = Unsafe.ReadUnaligned<ulong>(pUtf16Buffer);
+ if (!AllCharsInUInt64AreAscii(utf16Data64Bits))
+ {
+ goto FoundNonAsciiDataIn64BitRead;
+ }
+ }
+ else
+ {
+ utf16Data32BitsHigh = Unsafe.ReadUnaligned<uint>(pUtf16Buffer);
+ utf16Data32BitsLow = Unsafe.ReadUnaligned<uint>(pUtf16Buffer + 4 / sizeof(char));
+ if (!AllCharsInUInt32AreAscii(utf16Data32BitsHigh | utf16Data32BitsLow))
+ {
+ goto FoundNonAsciiDataIn64BitRead;
+ }
+ }
+
+ currentOffset = NarrowUtf16ToAscii_Sse2(pUtf16Buffer, pAsciiBuffer, elementCount);
+ }
+ }
+ else if (Vector.IsHardwareAccelerated)
+ {
+ uint SizeOfVector = (uint)Unsafe.SizeOf<Vector<byte>>(); // JIT will make this a const
+
+ // Only bother vectorizing if we have enough data to do so.
+ if (elementCount >= 2 * SizeOfVector)
+ {
+ // Since there's overhead to setting up the vectorized code path, we only want to
+ // call into it after a quick probe to ensure the next immediate characters really are ASCII.
+ // If we see non-ASCII data, we'll jump immediately to the draining logic at the end of the method.
+
+ if (IntPtr.Size >= 8)
+ {
+ utf16Data64Bits = Unsafe.ReadUnaligned<ulong>(pUtf16Buffer);
+ if (!AllCharsInUInt64AreAscii(utf16Data64Bits))
+ {
+ goto FoundNonAsciiDataIn64BitRead;
+ }
+ }
+ else
+ {
+ utf16Data32BitsHigh = Unsafe.ReadUnaligned<uint>(pUtf16Buffer);
+ utf16Data32BitsLow = Unsafe.ReadUnaligned<uint>(pUtf16Buffer + 4 / sizeof(char));
+ if (!AllCharsInUInt32AreAscii(utf16Data32BitsHigh | utf16Data32BitsLow))
+ {
+ goto FoundNonAsciiDataIn64BitRead;
+ }
+ }
+
+ Vector<ushort> maxAscii = new Vector<ushort>(0x007F);
+
+ nuint finalOffsetWhereCanLoop = elementCount - 2 * SizeOfVector;
+ do
+ {
+ Vector<ushort> utf16VectorHigh = Unsafe.ReadUnaligned<Vector<ushort>>(pUtf16Buffer + currentOffset);
+ Vector<ushort> utf16VectorLow = Unsafe.ReadUnaligned<Vector<ushort>>(pUtf16Buffer + currentOffset + Vector<ushort>.Count);
+
+ if (Vector.GreaterThanAny(Vector.BitwiseOr(utf16VectorHigh, utf16VectorLow), maxAscii))
+ {
+ break; // found non-ASCII data
+ }
+
+ // TODO: Is the below logic also valid for big-endian platforms?
+ Vector<byte> asciiVector = Vector.Narrow(utf16VectorHigh, utf16VectorLow);
+ Unsafe.WriteUnaligned<Vector<byte>>(pAsciiBuffer + currentOffset, asciiVector);
+
+ currentOffset += SizeOfVector;
+ } while (currentOffset <= finalOffsetWhereCanLoop);
+ }
+ }
+
+ Debug.Assert(currentOffset <= elementCount);
+ nuint remainingElementCount = elementCount - currentOffset;
+
+ // Try to narrow 64 bits -> 32 bits at a time.
+ // We needn't update remainingElementCount after this point.
+
+ if (remainingElementCount >= 4)
+ {
+ nuint finalOffsetWhereCanLoop = currentOffset + remainingElementCount - 4;
+ do
+ {
+ if (IntPtr.Size >= 8)
+ {
+ // Only perform QWORD reads on a 64-bit platform.
+ utf16Data64Bits = Unsafe.ReadUnaligned<ulong>(pUtf16Buffer + currentOffset);
+ if (!AllCharsInUInt64AreAscii(utf16Data64Bits))
+ {
+ goto FoundNonAsciiDataIn64BitRead;
+ }
+
+ NarrowFourUtf16CharsToAsciiAndWriteToBuffer(ref pAsciiBuffer[currentOffset], utf16Data64Bits);
+ }
+ else
+ {
+ utf16Data32BitsHigh = Unsafe.ReadUnaligned<uint>(pUtf16Buffer + currentOffset);
+ utf16Data32BitsLow = Unsafe.ReadUnaligned<uint>(pUtf16Buffer + currentOffset + 4 / sizeof(char));
+ if (!AllCharsInUInt32AreAscii(utf16Data32BitsHigh | utf16Data32BitsLow))
+ {
+ goto FoundNonAsciiDataIn64BitRead;
+ }
+
+ NarrowTwoUtf16CharsToAsciiAndWriteToBuffer(ref pAsciiBuffer[currentOffset], utf16Data32BitsHigh);
+ NarrowTwoUtf16CharsToAsciiAndWriteToBuffer(ref pAsciiBuffer[currentOffset + 2], utf16Data32BitsLow);
+ }
+
+ currentOffset += 4;
+ } while (currentOffset <= finalOffsetWhereCanLoop);
+ }
+
+ // Try to narrow 32 bits -> 16 bits.
+
+ if (((uint)remainingElementCount & 2) != 0)
+ {
+ utf16Data32BitsHigh = Unsafe.ReadUnaligned<uint>(pUtf16Buffer + currentOffset);
+ if (!AllCharsInUInt32AreAscii(utf16Data32BitsHigh))
+ {
+ goto FoundNonAsciiDataInHigh32Bits;
+ }
+
+ NarrowTwoUtf16CharsToAsciiAndWriteToBuffer(ref pAsciiBuffer[currentOffset], utf16Data32BitsHigh);
+ currentOffset += 2;
+ }
+
+ // Try to narrow 16 bits -> 8 bits.
+
+ if (((uint)remainingElementCount & 1) != 0)
+ {
+ utf16Data32BitsHigh = pUtf16Buffer[currentOffset];
+ if (utf16Data32BitsHigh <= 0x007Fu)
+ {
+ pAsciiBuffer[currentOffset] = (byte)utf16Data32BitsHigh;
+ currentOffset++;
+ }
+ }
+
+ Finish:
+
+ return currentOffset;
+
+ FoundNonAsciiDataIn64BitRead:
+
+ if (IntPtr.Size >= 8)
+ {
+ // Try checking the first 32 bits of the buffer for non-ASCII data.
+ // Regardless, we'll move the non-ASCII data into the utf16Data32BitsHigh local.
+
+ if (BitConverter.IsLittleEndian)
+ {
+ utf16Data32BitsHigh = (uint)utf16Data64Bits;
+ }
+ else
+ {
+ utf16Data32BitsHigh = (uint)(utf16Data64Bits >> 32);
+ }
+
+ if (AllCharsInUInt32AreAscii(utf16Data32BitsHigh))
+ {
+ NarrowTwoUtf16CharsToAsciiAndWriteToBuffer(ref pAsciiBuffer[currentOffset], utf16Data32BitsHigh);
+
+ if (BitConverter.IsLittleEndian)
+ {
+ utf16Data32BitsHigh = (uint)(utf16Data64Bits >> 32);
+ }
+ else
+ {
+ utf16Data32BitsHigh = (uint)utf16Data64Bits;
+ }
+
+ currentOffset += 2;
+ }
+ }
+ else
+ {
+ // Need to determine if the high or the low 32-bit value contained non-ASCII data.
+ // Regardless, we'll move the non-ASCII data into the utf16Data32BitsHigh local.
+
+ if (AllCharsInUInt32AreAscii(utf16Data32BitsHigh))
+ {
+ NarrowTwoUtf16CharsToAsciiAndWriteToBuffer(ref pAsciiBuffer[currentOffset], utf16Data32BitsHigh);
+ utf16Data32BitsHigh = utf16Data32BitsLow;
+ currentOffset += 2;
+ }
+ }
+
+ FoundNonAsciiDataInHigh32Bits:
+
+ Debug.Assert(!AllCharsInUInt32AreAscii(utf16Data32BitsHigh), "Shouldn't have reached this point if we have an all-ASCII input.");
+
+ // There's at most one char that needs to be drained.
+
+ if (FirstCharInUInt32IsAscii(utf16Data32BitsHigh))
+ {
+ if (!BitConverter.IsLittleEndian)
+ {
+ utf16Data32BitsHigh >>= 16; // move high char down to low char
+ }
+
+ pAsciiBuffer[currentOffset] = (byte)utf16Data32BitsHigh;
+ currentOffset++;
+ }
+
+ goto Finish;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveOptimization)]
+ private static unsafe nuint NarrowUtf16ToAscii_Sse2(char* pUtf16Buffer, byte* pAsciiBuffer, nuint elementCount)
+ {
+ // This method contains logic optimized for both SSE2 and SSE41. Much of the logic in this method
+ // will be elided by JIT once we determine which specific ISAs we support.
+
+ // JIT turns the below into constants
+
+ uint SizeOfVector128 = (uint)Unsafe.SizeOf<Vector128<byte>>();
+ nuint MaskOfAllBitsInVector128 = (nuint)(SizeOfVector128 - 1);
+
+ // This method is written such that control generally flows top-to-bottom, avoiding
+ // jumps as much as possible in the optimistic case of "all ASCII". If we see non-ASCII
+ // data, we jump out of the hot paths to targets at the end of the method.
+
+ Debug.Assert(Sse2.IsSupported);
+ Debug.Assert(BitConverter.IsLittleEndian);
+ Debug.Assert(elementCount >= 2 * SizeOfVector128);
+
+ Vector128<short> asciiMaskForPTEST = Vector128.Create(unchecked((short)0xFF80)); // used for PTEST on supported hardware
+ Vector128<short> asciiMaskForPXOR = Vector128.Create(unchecked((short)0x8000)); // used for PXOR
+ Vector128<short> asciiMaskForPCMPGTW = Vector128.Create(unchecked((short)0x807F)); // used for PCMPGTW
+
+ // First, perform an unaligned read of the first part of the input buffer.
+
+ Vector128<short> utf16VectorFirst = Sse2.LoadVector128((short*)pUtf16Buffer); // unaligned load
+
+ // If there's non-ASCII data in the first 8 elements of the vector, there's nothing we can do.
+ // See comments in GetIndexOfFirstNonAsciiChar_Sse2 for information about how this works.
+
+ if (Sse41.IsSupported)
+ {
+ if (!Sse41.TestZ(utf16VectorFirst, asciiMaskForPTEST))
+ {
+ return 0;
+ }
+ }
+ else
+ {
+ if (Sse2.MoveMask(Sse2.CompareGreaterThan(Sse2.Xor(utf16VectorFirst, asciiMaskForPXOR), asciiMaskForPCMPGTW).AsByte()) != 0)
+ {
+ return 0;
+ }
+ }
+
+ // Turn the 8 ASCII chars we just read into 8 ASCII bytes, then copy it to the destination.
+
+ Vector128<byte> asciiVector = Sse2.PackUnsignedSaturate(utf16VectorFirst, utf16VectorFirst);
+
+ if (Sse41.X64.IsSupported)
+ {
+ // Use PEXTRQ instruction if available, since it can extract from the vector directly to the destination address.
+ Unsafe.WriteUnaligned<ulong>(pAsciiBuffer, Sse41.X64.Extract(asciiVector.AsUInt64(), 0));
+ }
+ else
+ {
+ // Bounce this through a temporary register (with potential stack spillage) before writing to memory.
+ Unsafe.WriteUnaligned<ulong>(pAsciiBuffer, asciiVector.AsUInt64().GetElement(0));
+ }
+
+ nuint currentOffsetInElements = SizeOfVector128 / 2; // we processed 8 elements so far
+
+ // We're going to get the best performance when we have aligned writes, so we'll take the
+ // hit of potentially unaligned reads in order to hit this sweet spot.
+
+ // pAsciiBuffer points to the start of the destination buffer, immediately before where we wrote
+ // the 8 bytes previously. If the 0x08 bit is set at the pinned address, then the 8 bytes we wrote
+ // previously mean that the 0x08 bit is *not* set at address &pAsciiBuffer[SizeOfVector128 / 2]. In
+ // that case we can immediately back up to the previous aligned boundary and start the main loop.
+ // If the 0x08 bit is *not* set at the pinned address, then it means the 0x08 bit *is* set at
+ // address &pAsciiBuffer[SizeOfVector128 / 2], and we should perform one more 8-byte write to bump
+ // just past the next aligned boundary address.
+
+ if (((uint)pAsciiBuffer & (SizeOfVector128 / 2)) == 0)
+ {
+ // We need to perform one more partial vector write before we can get the alignment we want.
+
+ utf16VectorFirst = Sse2.LoadVector128((short*)pUtf16Buffer + currentOffsetInElements); // unaligned load
+
+ // See comments earlier in this method for information about how this works.
+ if (Sse41.IsSupported)
+ {
+ if (!Sse41.TestZ(utf16VectorFirst, asciiMaskForPTEST))
+ {
+ goto Finish;
+ }
+ }
+ else
+ {
+ if (Sse2.MoveMask(Sse2.CompareGreaterThan(Sse2.Xor(utf16VectorFirst, asciiMaskForPXOR), asciiMaskForPCMPGTW).AsByte()) != 0)
+ {
+ goto Finish;
+ }
+ }
+
+ // Turn the 8 ASCII chars we just read into 8 ASCII bytes, then copy it to the destination.
+ asciiVector = Sse2.PackUnsignedSaturate(utf16VectorFirst, utf16VectorFirst);
+
+ // See comments earlier in this method for information about how this works.
+ if (Sse41.X64.IsSupported)
+ {
+ Unsafe.WriteUnaligned<ulong>(pAsciiBuffer + currentOffsetInElements, Sse41.X64.Extract(asciiVector.AsUInt64(), 0));
+ }
+ else
+ {
+ Unsafe.WriteUnaligned<ulong>(pAsciiBuffer + currentOffsetInElements, asciiVector.AsUInt64().GetElement(0));
+ }
+ }
+
+ // Calculate how many elements we wrote in order to get pAsciiBuffer to its next alignment
+ // point, then use that as the base offset going forward.
+
+ currentOffsetInElements = SizeOfVector128 - ((nuint)pAsciiBuffer & MaskOfAllBitsInVector128);
+ Debug.Assert(0 < currentOffsetInElements && currentOffsetInElements <= SizeOfVector128, "We wrote at least 1 byte but no more than a whole vector.");
+
+ Debug.Assert(currentOffsetInElements <= elementCount, "Shouldn't have overrun the destination buffer.");
+ Debug.Assert(elementCount - currentOffsetInElements >= SizeOfVector128, "We should be able to run at least one whole vector.");
+
+ nuint finalOffsetWhereCanRunLoop = elementCount - SizeOfVector128;
+ do
+ {
+ // In a loop, perform two unaligned reads, narrow to a single vector, then aligned write one vector.
+
+ utf16VectorFirst = Sse2.LoadVector128((short*)pUtf16Buffer + currentOffsetInElements); // unaligned load
+ Vector128<short> utf16VectorSecond = Sse2.LoadVector128((short*)pUtf16Buffer + currentOffsetInElements + SizeOfVector128 / sizeof(short)); // unaligned load
+ Vector128<short> combinedVector = Sse2.Or(utf16VectorFirst, utf16VectorSecond);
+
+ // See comments in GetIndexOfFirstNonAsciiChar_Sse2 for information about how this works.
+ if (Sse41.IsSupported)
+ {
+ if (!Sse41.TestZ(combinedVector, asciiMaskForPTEST))
+ {
+ goto FoundNonAsciiDataInLoop;
+ }
+ }
+ else
+ {
+ if (Sse2.MoveMask(Sse2.CompareGreaterThan(Sse2.Xor(combinedVector, asciiMaskForPXOR), asciiMaskForPCMPGTW).AsByte()) != 0)
+ {
+ goto FoundNonAsciiDataInLoop;
+ }
+ }
+
+ // Build up the UTF-8 vector and perform the store.
+
+ asciiVector = Sse2.PackUnsignedSaturate(utf16VectorFirst, utf16VectorSecond);
+
+ Debug.Assert(((nuint)pAsciiBuffer + currentOffsetInElements) % SizeOfVector128 == 0, "Write should be aligned.");
+ Sse2.StoreAligned(pAsciiBuffer + currentOffsetInElements, asciiVector); // aligned
+
+ currentOffsetInElements += SizeOfVector128;
+ } while (currentOffsetInElements <= finalOffsetWhereCanRunLoop);
+
+ Finish:
+
+ // There might be some ASCII data left over. That's fine - we'll let our caller handle the final drain.
+ return currentOffsetInElements;
+
+ FoundNonAsciiDataInLoop:
+
+ // Can we at least narrow the high vector?
+ // See comments in GetIndexOfFirstNonAsciiChar_Sse2 for information about how this works.
+ if (Sse41.IsSupported)
+ {
+ if (!Sse41.TestZ(utf16VectorFirst, asciiMaskForPTEST))
+ {
+ goto Finish; // found non-ASCII data
+ }
+ }
+ else
+ {
+ if (Sse2.MoveMask(Sse2.CompareGreaterThan(Sse2.Xor(utf16VectorFirst, asciiMaskForPXOR), asciiMaskForPCMPGTW).AsByte()) != 0)
+ {
+ goto Finish; // found non-ASCII data
+ }
+ }
+
+ // First part was all ASCII, narrow and aligned write. Note we're only filling in the low half of the vector.
+ asciiVector = Sse2.PackUnsignedSaturate(utf16VectorFirst, utf16VectorFirst);
+
+ Debug.Assert(((nuint)pAsciiBuffer + currentOffsetInElements) % sizeof(ulong) == 0, "Destination should be ulong-aligned.");
+
+ // See comments earlier in this method for information about how this works.
+ if (Sse41.X64.IsSupported)
+ {
+ *(ulong*)(pAsciiBuffer + currentOffsetInElements) = Sse41.X64.Extract(asciiVector.AsUInt64(), 0);
+ }
+ else
+ {
+ *(ulong*)(pAsciiBuffer + currentOffsetInElements) = asciiVector.AsUInt64().GetElement(0);
+ }
+ currentOffsetInElements += SizeOfVector128 / 2;
+
+ goto Finish;
+ }
+
+ /// <summary>
+ /// Rotates a <see cref="uint"/> left. The JIT is smart enough to turn this into a ROL / ROR instruction.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static uint ROL32(uint value, int shift) => (value << shift) | (value >> (32 - shift));
+
+ /// <summary>
+ /// Copies as many ASCII bytes (00..7F) as possible from <paramref name="pAsciiBuffer"/>
+ /// to <paramref name="pUtf16Buffer"/>, stopping when the first non-ASCII byte is encountered
+ /// or once <paramref name="elementCount"/> elements have been converted. Returns the total number
+ /// of elements that were able to be converted.
+ /// </summary>
+ public static unsafe nuint WidenAsciiToUtf16(byte* pAsciiBuffer, char* pUtf16Buffer, nuint elementCount)
+ {
+ nuint currentOffset = 0;
+
+ // If SSE2 is supported, use those specific intrinsics instead of the generic vectorized
+ // code below. This has two benefits: (a) we can take advantage of specific instructions like
+ // pmovmskb which we know are optimized, and (b) we can avoid downclocking the processor while
+ // this method is running.
+
+ if (Sse2.IsSupported)
+ {
+ if (elementCount >= 2 * (uint)Unsafe.SizeOf<Vector128<byte>>())
+ {
+ currentOffset = WidenAsciiToUtf16_Sse2(pAsciiBuffer, pUtf16Buffer, elementCount);
+ }
+ }
+ else if (Vector.IsHardwareAccelerated)
+ {
+ uint SizeOfVector = (uint)Unsafe.SizeOf<Vector<byte>>(); // JIT will make this a const
+
+ // Only bother vectorizing if we have enough data to do so.
+ if (elementCount >= SizeOfVector)
+ {
+ // Note use of SBYTE instead of BYTE below; we're using the two's-complement
+ // representation of negative integers to act as a surrogate for "is ASCII?".
+
+ nuint finalOffsetWhereCanLoop = elementCount - SizeOfVector;
+ do
+ {
+ Vector<sbyte> asciiVector = Unsafe.ReadUnaligned<Vector<sbyte>>(pAsciiBuffer + currentOffset);
+ if (Vector.LessThanAny(asciiVector, Vector<sbyte>.Zero))
+ {
+ break; // found non-ASCII data
+ }
+
+ Vector.Widen(Vector.AsVectorByte(asciiVector), out Vector<ushort> utf16LowVector, out Vector<ushort> utf16HighVector);
+
+ // TODO: Is the below logic also valid for big-endian platforms?
+ Unsafe.WriteUnaligned<Vector<ushort>>(pUtf16Buffer + currentOffset, utf16LowVector);
+ Unsafe.WriteUnaligned<Vector<ushort>>(pUtf16Buffer + currentOffset + Vector<ushort>.Count, utf16HighVector);
+
+ currentOffset += SizeOfVector;
+ } while (currentOffset <= finalOffsetWhereCanLoop);
+ }
+ }
+
+ Debug.Assert(currentOffset <= elementCount);
+ nuint remainingElementCount = elementCount - currentOffset;
+
+ // Try to widen 32 bits -> 64 bits at a time.
+ // We needn't update remainingElementCount after this point.
+
+ uint asciiData;
+
+ if (remainingElementCount >= 4)
+ {
+ nuint finalOffsetWhereCanLoop = currentOffset + remainingElementCount - 4;
+ do
+ {
+ asciiData = Unsafe.ReadUnaligned<uint>(pAsciiBuffer + currentOffset);
+ if (!AllBytesInUInt32AreAscii(asciiData))
+ {
+ goto FoundNonAsciiData;
+ }
+
+ WidenFourAsciiBytesToUtf16AndWriteToBuffer(ref pUtf16Buffer[currentOffset], asciiData);
+ currentOffset += 4;
+ } while (currentOffset <= finalOffsetWhereCanLoop);
+ }
+
+ // Try to widen 16 bits -> 32 bits.
+
+ if (((uint)remainingElementCount & 2) != 0)
+ {
+ asciiData = Unsafe.ReadUnaligned<ushort>(pAsciiBuffer + currentOffset);
+ if (!AllBytesInUInt32AreAscii(asciiData))
+ {
+ goto FoundNonAsciiData;
+ }
+
+ if (BitConverter.IsLittleEndian)
+ {
+ pUtf16Buffer[currentOffset] = (char)(byte)asciiData;
+ pUtf16Buffer[currentOffset + 1] = (char)(asciiData >> 8);
+ }
+ else
+ {
+ pUtf16Buffer[currentOffset + 1] = (char)(byte)asciiData;
+ pUtf16Buffer[currentOffset] = (char)(asciiData >> 8);
+ }
+
+ currentOffset += 2;
+ }
+
+ // Try to widen 8 bits -> 16 bits.
+
+ if (((uint)remainingElementCount & 1) != 0)
+ {
+ asciiData = pAsciiBuffer[currentOffset];
+ if (((byte)asciiData & 0x80) != 0)
+ {
+ goto Finish;
+ }
+
+ pUtf16Buffer[currentOffset] = (char)asciiData;
+ currentOffset += 1;
+ }
+
+ Finish:
+
+ return currentOffset;
+
+ FoundNonAsciiData:
+
+ Debug.Assert(!AllBytesInUInt32AreAscii(asciiData), "Shouldn't have reached this point if we have an all-ASCII input.");
+
+ // Drain ASCII bytes one at a time.
+
+ while (((byte)asciiData & 0x80) == 0)
+ {
+ pUtf16Buffer[currentOffset] = (char)(byte)asciiData;
+ currentOffset += 1;
+ asciiData >>= 8;
+ }
+
+ goto Finish;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveOptimization)]
+ private static unsafe nuint WidenAsciiToUtf16_Sse2(byte* pAsciiBuffer, char* pUtf16Buffer, nuint elementCount)
+ {
+ // JIT turns the below into constants
+
+ uint SizeOfVector128 = (uint)Unsafe.SizeOf<Vector128<byte>>();
+ nuint MaskOfAllBitsInVector128 = (nuint)(SizeOfVector128 - 1);
+
+ // This method is written such that control generally flows top-to-bottom, avoiding
+ // jumps as much as possible in the optimistic case of "all ASCII". If we see non-ASCII
+ // data, we jump out of the hot paths to targets at the end of the method.
+
+ Debug.Assert(Sse2.IsSupported);
+ Debug.Assert(BitConverter.IsLittleEndian);
+ Debug.Assert(elementCount >= 2 * SizeOfVector128);
+
+ // We're going to get the best performance when we have aligned writes, so we'll take the
+ // hit of potentially unaligned reads in order to hit this sweet spot.
+
+ Vector128<byte> asciiVector;
+ Vector128<byte> utf16FirstHalfVector;
+ uint mask;
+
+ // First, perform an unaligned read of the first part of the input buffer.
+
+ asciiVector = Sse2.LoadVector128(pAsciiBuffer); // unaligned load
+ mask = (uint)Sse2.MoveMask(asciiVector);
+
+ // If there's non-ASCII data in the first 8 elements of the vector, there's nothing we can do.
+
+ if ((byte)mask != 0)
+ {
+ return 0;
+ }
+
+ // Then perform an unaligned write of the first part of the input buffer.
+
+ Vector128<byte> zeroVector = Vector128<byte>.Zero;
+
+ utf16FirstHalfVector = Sse2.UnpackLow(asciiVector, zeroVector);
+ Sse2.Store((byte*)pUtf16Buffer, utf16FirstHalfVector); // unaligned
+
+ // Calculate how many elements we wrote in order to get pOutputBuffer to its next alignment
+ // point, then use that as the base offset going forward. Remember the >> 1 to account for
+ // that we wrote chars, not bytes. This means we may re-read data in the next iteration of
+ // the loop, but this is ok.
+
+ nuint currentOffset = (SizeOfVector128 >> 1) - (((nuint)pUtf16Buffer >> 1) & (MaskOfAllBitsInVector128 >> 1));
+ Debug.Assert(0 < currentOffset && currentOffset <= SizeOfVector128 / sizeof(char));
+
+ nuint finalOffsetWhereCanRunLoop = elementCount - SizeOfVector128;
+
+ do
+ {
+ // In a loop, perform an unaligned read, widen to two vectors, then aligned write the two vectors.
+
+ asciiVector = Sse2.LoadVector128(pAsciiBuffer + currentOffset); // unaligned load
+ mask = (uint)Sse2.MoveMask(asciiVector);
+
+ if (mask != 0)
+ {
+ // non-ASCII byte somewhere
+ goto NonAsciiDataSeenInInnerLoop;
+ }
+
+ byte* pStore = (byte*)(pUtf16Buffer + currentOffset);
+ Sse2.StoreAligned(pStore, Sse2.UnpackLow(asciiVector, zeroVector));
+
+ pStore += SizeOfVector128;
+ Sse2.StoreAligned(pStore, Sse2.UnpackHigh(asciiVector, zeroVector));
+
+ currentOffset += SizeOfVector128;
+ } while (currentOffset <= finalOffsetWhereCanRunLoop);
+
+ Finish:
+
+ return currentOffset;
+
+ NonAsciiDataSeenInInnerLoop:
+
+ // Can we at least widen the first part of the vector?
+
+ if ((byte)mask == 0)
+ {
+ // First part was all ASCII, widen
+ utf16FirstHalfVector = Sse2.UnpackLow(asciiVector, zeroVector);
+ Sse2.StoreAligned((byte*)(pUtf16Buffer + currentOffset), utf16FirstHalfVector);
+ currentOffset += SizeOfVector128 / 2;
+ }
+
+ goto Finish;
+ }
+
+ /// <summary>
+ /// Given a DWORD which represents a buffer of 4 bytes, widens the buffer into 4 WORDs and
+ /// writes them to the output buffer with machine endianness.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ private static void WidenFourAsciiBytesToUtf16AndWriteToBuffer(ref char outputBuffer, uint value)
+ {
+ Debug.Assert(AllBytesInUInt32AreAscii(value));
+
+ if (Bmi2.X64.IsSupported)
+ {
+ // BMI2 will work regardless of the processor's endianness.
+ Unsafe.WriteUnaligned(ref Unsafe.As<char, byte>(ref outputBuffer), Bmi2.X64.ParallelBitDeposit(value, 0x00FF00FF_00FF00FFul));
+ }
+ else
+ {
+ if (BitConverter.IsLittleEndian)
+ {
+ outputBuffer = (char)(byte)value;
+ value >>= 8;
+ Unsafe.Add(ref outputBuffer, 1) = (char)(byte)value;
+ value >>= 8;
+ Unsafe.Add(ref outputBuffer, 2) = (char)(byte)value;
+ value >>= 8;
+ Unsafe.Add(ref outputBuffer, 3) = (char)value;
+ }
+ else
{
- break;
+ Unsafe.Add(ref outputBuffer, 3) = (char)(byte)value;
+ value >>= 8;
+ Unsafe.Add(ref outputBuffer, 2) = (char)(byte)value;
+ value >>= 8;
+ Unsafe.Add(ref outputBuffer, 1) = (char)(byte)value;
+ value >>= 8;
+ outputBuffer = (char)value;
}
- pChars[idx] = (char)b;
}
- return idx;
}
}
}
diff --git a/src/System.Private.CoreLib/shared/System/Text/DecoderFallback.cs b/src/System.Private.CoreLib/shared/System/Text/DecoderFallback.cs
index 2eb03d8089..d5782d58e4 100644
--- a/src/System.Private.CoreLib/shared/System/Text/DecoderFallback.cs
+++ b/src/System.Private.CoreLib/shared/System/Text/DecoderFallback.cs
@@ -278,7 +278,7 @@ namespace System.Text
Rune thisRune;
while ((thisRune = GetNextRune()).Value != 0)
{
- if (thisRune.TryEncode(chars, out int charsWrittenJustNow))
+ if (thisRune.TryEncodeToUtf16(chars, out int charsWrittenJustNow))
{
chars = chars.Slice(charsWrittenJustNow);
continue;
diff --git a/src/System.Private.CoreLib/shared/System/Text/DecoderNLS.cs b/src/System.Private.CoreLib/shared/System/Text/DecoderNLS.cs
index 597d362bf7..9040a94f0f 100644
--- a/src/System.Private.CoreLib/shared/System/Text/DecoderNLS.cs
+++ b/src/System.Private.CoreLib/shared/System/Text/DecoderNLS.cs
@@ -332,7 +332,7 @@ namespace System.Text
switch (_encoding.DecodeFirstRune(combinedBuffer, out Rune value, out int combinedBufferBytesConsumed))
{
case OperationStatus.Done:
- if (value.TryEncode(chars, out charsWritten))
+ if (value.TryEncodeToUtf16(chars, out charsWritten))
{
goto Finish; // successfully transcoded bytes -> chars
}
diff --git a/src/System.Private.CoreLib/shared/System/Text/Encoding.Internal.cs b/src/System.Private.CoreLib/shared/System/Text/Encoding.Internal.cs
index 09044afefe..0e32167957 100644
--- a/src/System.Private.CoreLib/shared/System/Text/Encoding.Internal.cs
+++ b/src/System.Private.CoreLib/shared/System/Text/Encoding.Internal.cs
@@ -327,7 +327,7 @@ namespace System.Text
// There are two scenarios: (a) the source buffer contained invalid / incomplete UTF-16 data;
// or (b) the encoding can't translate this scalar value.
- if (Rune.DecodeUtf16(chars, out Rune firstScalarValue, out int charsConsumedThisIteration) == OperationStatus.NeedMoreData
+ if (Rune.DecodeFromUtf16(chars, out Rune firstScalarValue, out int charsConsumedThisIteration) == OperationStatus.NeedMoreData
&& encoder != null
&& !encoder.MustFlush)
{
@@ -603,7 +603,7 @@ namespace System.Text
// There are two scenarios: (a) the source buffer contained invalid / incomplete UTF-16 data;
// or (b) the encoding can't translate this scalar value.
- switch (Rune.DecodeUtf16(chars, out Rune firstScalarValue, out int charsConsumedThisIteration))
+ switch (Rune.DecodeFromUtf16(chars, out Rune firstScalarValue, out int charsConsumedThisIteration))
{
case OperationStatus.NeedMoreData:
Debug.Assert(charsConsumedThisIteration == chars.Length, "If returning NeedMoreData, should out the entire buffer length as chars consumed.");
diff --git a/src/System.Private.CoreLib/shared/System/Text/Rune.cs b/src/System.Private.CoreLib/shared/System/Text/Rune.cs
index dc3c892ac6..a91c0fcb99 100644
--- a/src/System.Private.CoreLib/shared/System/Text/Rune.cs
+++ b/src/System.Private.CoreLib/shared/System/Text/Rune.cs
@@ -231,20 +231,20 @@ namespace System.Text
/// </para>
/// </returns>
/// <remarks>
- /// The general calling convention is to call this method in a loop, slicing the <paramref name="utf16Source"/> buffer by
+ /// The general calling convention is to call this method in a loop, slicing the <paramref name="source"/> buffer by
/// <paramref name="charsConsumed"/> elements on each iteration of the loop. On each iteration of the loop <paramref name="result"/>
/// will contain the real scalar value if successfully decoded, or it will contain <see cref="ReplacementChar"/> if
/// the data could not be successfully decoded. This pattern provides convenient automatic U+FFFD substitution of
/// invalid sequences while iterating through the loop.
/// </remarks>
- public static OperationStatus DecodeUtf16(ReadOnlySpan<char> utf16Source, out Rune result, out int charsConsumed)
+ public static OperationStatus DecodeFromUtf16(ReadOnlySpan<char> source, out Rune result, out int charsConsumed)
{
- if (!utf16Source.IsEmpty)
+ if (!source.IsEmpty)
{
// First, check for the common case of a BMP scalar value.
// If this is correct, return immediately.
- char firstChar = utf16Source[0];
+ char firstChar = source[0];
if (TryCreate(firstChar, out result))
{
charsConsumed = 1;
@@ -255,9 +255,9 @@ namespace System.Text
// Let's optimistically assume for now it's a high surrogate and hope
// that combining it with the next char yields useful results.
- if (1 < (uint)utf16Source.Length)
+ if (1 < (uint)source.Length)
{
- char secondChar = utf16Source[1];
+ char secondChar = source[1];
if (TryCreate(firstChar, secondChar, out result))
{
// Success! Formed a supplementary scalar value.
@@ -284,7 +284,7 @@ namespace System.Text
// If we got to this point, the input buffer was empty, or the buffer
// was a single element in length and that element was a high surrogate char.
- charsConsumed = utf16Source.Length;
+ charsConsumed = source.Length;
result = ReplacementChar;
return OperationStatus.NeedMoreData;
@@ -296,66 +296,6 @@ namespace System.Text
}
/// <summary>
- /// Decodes the <see cref="Rune"/> at the end of the provided UTF-16 source buffer.
- /// </summary>
- /// <remarks>
- /// This method is very similar to <see cref="DecodeUtf16(ReadOnlySpan{char}, out Rune, out int)"/>, but it allows
- /// the caller to loop backward instead of forward. The typical calling convention is that on each iteration
- /// of the loop, the caller should slice off the final <paramref name="charsConsumed"/> elements of
- /// the <paramref name="utf16Source"/> buffer.
- /// </remarks>
- public static OperationStatus DecodeUtf16FromEnd(ReadOnlySpan<char> utf16Source, out Rune result, out int charsConsumed)
- {
- int index = utf16Source.Length - 1;
- if ((uint)index < (uint)utf16Source.Length)
- {
- // First, check for the common case of a BMP scalar value.
- // If this is correct, return immediately.
-
- char finalChar = utf16Source[index];
- if (TryCreate(finalChar, out result))
- {
- charsConsumed = 1;
- return OperationStatus.Done;
- }
-
- if (char.IsLowSurrogate(finalChar))
- {
- // The final character was a UTF-16 low surrogate code point.
- // This must be preceded by a UTF-16 high surrogate code point, otherwise
- // we have a standalone low surrogate, which is always invalid.
-
- index--;
- if ((uint)index < (uint)utf16Source.Length)
- {
- char penultimateChar = utf16Source[index];
- if (TryCreate(penultimateChar, finalChar, out result))
- {
- // Success! Formed a supplementary scalar value.
- charsConsumed = 2;
- return OperationStatus.Done;
- }
- }
-
- // If we got to this point, we saw a standalone low surrogate
- // and must report an error.
-
- charsConsumed = 1; // standalone surrogate
- result = ReplacementChar;
- return OperationStatus.InvalidData;
- }
- }
-
- // If we got this far, the source buffer was empty, or the source buffer ended
- // with a UTF-16 high surrogate code point. These aren't errors since they could
- // be valid given more input data.
-
- charsConsumed = (int)((uint)(-utf16Source.Length) >> 31); // 0 -> 0, all other lengths -> 1
- result = ReplacementChar;
- return OperationStatus.NeedMoreData;
- }
-
- /// <summary>
/// Decodes the <see cref="Rune"/> at the beginning of the provided UTF-8 source buffer.
/// </summary>
/// <returns>
@@ -375,13 +315,13 @@ namespace System.Text
/// </para>
/// </returns>
/// <remarks>
- /// The general calling convention is to call this method in a loop, slicing the <paramref name="utf8Source"/> buffer by
+ /// The general calling convention is to call this method in a loop, slicing the <paramref name="source"/> buffer by
/// <paramref name="bytesConsumed"/> elements on each iteration of the loop. On each iteration of the loop <paramref name="result"/>
/// will contain the real scalar value if successfully decoded, or it will contain <see cref="ReplacementChar"/> if
/// the data could not be successfully decoded. This pattern provides convenient automatic U+FFFD substitution of
/// invalid sequences while iterating through the loop.
/// </remarks>
- public static OperationStatus DecodeUtf8(ReadOnlySpan<byte> utf8Source, out Rune result, out int bytesConsumed)
+ public static OperationStatus DecodeFromUtf8(ReadOnlySpan<byte> source, out Rune result, out int bytesConsumed)
{
// This method follows the Unicode Standard's recommendation for detecting
// the maximal subpart of an ill-formed subsequence. See The Unicode Standard,
@@ -393,12 +333,12 @@ namespace System.Text
// Try reading input[0].
- if ((uint)index >= (uint)utf8Source.Length)
+ if ((uint)index >= (uint)source.Length)
{
goto NeedsMoreData;
}
- uint tempValue = utf8Source[index];
+ uint tempValue = source[index];
if (!UnicodeUtility.IsAsciiCodePoint(tempValue))
{
goto NotAscii;
@@ -428,7 +368,7 @@ namespace System.Text
// Try reading input[1].
index++;
- if ((uint)index >= (uint)utf8Source.Length)
+ if ((uint)index >= (uint)source.Length)
{
goto NeedsMoreData;
}
@@ -437,7 +377,7 @@ namespace System.Text
// complement representation is in the range [-65..-128]. This allows us to
// perform a single comparison to see if a byte is a continuation byte.
- int thisByteSignExtended = (sbyte)utf8Source[index];
+ int thisByteSignExtended = (sbyte)source[index];
if (thisByteSignExtended >= -64)
{
goto Invalid;
@@ -482,12 +422,12 @@ namespace System.Text
// Try reading input[2].
index++;
- if ((uint)index >= (uint)utf8Source.Length)
+ if ((uint)index >= (uint)source.Length)
{
goto NeedsMoreData;
}
- thisByteSignExtended = (sbyte)utf8Source[index];
+ thisByteSignExtended = (sbyte)source[index];
if (thisByteSignExtended >= -64)
{
goto Invalid; // this byte is not a UTF-8 continuation byte
@@ -507,12 +447,12 @@ namespace System.Text
// Try reading input[3].
index++;
- if ((uint)index >= (uint)utf8Source.Length)
+ if ((uint)index >= (uint)source.Length)
{
goto NeedsMoreData;
}
- thisByteSignExtended = (sbyte)utf8Source[index];
+ thisByteSignExtended = (sbyte)source[index];
if (thisByteSignExtended >= -64)
{
goto Invalid; // this byte is not a UTF-8 continuation byte
@@ -546,23 +486,83 @@ namespace System.Text
}
/// <summary>
+ /// Decodes the <see cref="Rune"/> at the end of the provided UTF-16 source buffer.
+ /// </summary>
+ /// <remarks>
+ /// This method is very similar to <see cref="DecodeFromUtf16(ReadOnlySpan{char}, out Rune, out int)"/>, but it allows
+ /// the caller to loop backward instead of forward. The typical calling convention is that on each iteration
+ /// of the loop, the caller should slice off the final <paramref name="charsConsumed"/> elements of
+ /// the <paramref name="source"/> buffer.
+ /// </remarks>
+ public static OperationStatus DecodeLastFromUtf16(ReadOnlySpan<char> source, out Rune result, out int charsConsumed)
+ {
+ int index = source.Length - 1;
+ if ((uint)index < (uint)source.Length)
+ {
+ // First, check for the common case of a BMP scalar value.
+ // If this is correct, return immediately.
+
+ char finalChar = source[index];
+ if (TryCreate(finalChar, out result))
+ {
+ charsConsumed = 1;
+ return OperationStatus.Done;
+ }
+
+ if (char.IsLowSurrogate(finalChar))
+ {
+ // The final character was a UTF-16 low surrogate code point.
+ // This must be preceded by a UTF-16 high surrogate code point, otherwise
+ // we have a standalone low surrogate, which is always invalid.
+
+ index--;
+ if ((uint)index < (uint)source.Length)
+ {
+ char penultimateChar = source[index];
+ if (TryCreate(penultimateChar, finalChar, out result))
+ {
+ // Success! Formed a supplementary scalar value.
+ charsConsumed = 2;
+ return OperationStatus.Done;
+ }
+ }
+
+ // If we got to this point, we saw a standalone low surrogate
+ // and must report an error.
+
+ charsConsumed = 1; // standalone surrogate
+ result = ReplacementChar;
+ return OperationStatus.InvalidData;
+ }
+ }
+
+ // If we got this far, the source buffer was empty, or the source buffer ended
+ // with a UTF-16 high surrogate code point. These aren't errors since they could
+ // be valid given more input data.
+
+ charsConsumed = (int)((uint)(-source.Length) >> 31); // 0 -> 0, all other lengths -> 1
+ result = ReplacementChar;
+ return OperationStatus.NeedMoreData;
+ }
+
+ /// <summary>
/// Decodes the <see cref="Rune"/> at the end of the provided UTF-8 source buffer.
/// </summary>
/// <remarks>
- /// This method is very similar to <see cref="DecodeUtf8(ReadOnlySpan{byte}, out Rune, out int)"/>, but it allows
+ /// This method is very similar to <see cref="DecodeFromUtf8(ReadOnlySpan{byte}, out Rune, out int)"/>, but it allows
/// the caller to loop backward instead of forward. The typical calling convention is that on each iteration
/// of the loop, the caller should slice off the final <paramref name="bytesConsumed"/> elements of
- /// the <paramref name="utf8Source"/> buffer.
+ /// the <paramref name="source"/> buffer.
/// </remarks>
- public static OperationStatus DecodeUtf8FromEnd(ReadOnlySpan<byte> utf8Source, out Rune value, out int bytesConsumed)
+ public static OperationStatus DecodeLastFromUtf8(ReadOnlySpan<byte> source, out Rune value, out int bytesConsumed)
{
- int index = utf8Source.Length - 1;
- if ((uint)index < (uint)utf8Source.Length)
+ int index = source.Length - 1;
+ if ((uint)index < (uint)source.Length)
{
// The buffer contains at least one byte. Let's check the fast case where the
// buffer ends with an ASCII byte.
- uint tempValue = utf8Source[index];
+ uint tempValue = source[index];
if (UnicodeUtility.IsAsciiCodePoint(tempValue))
{
bytesConsumed = 1;
@@ -582,7 +582,7 @@ namespace System.Text
// This is a UTF-8 leading byte. We'll do a forward read from here.
// It'll return invalid (if given C0, F5, etc.) or incomplete. Both are fine.
- return DecodeUtf8(utf8Source.Slice(index), out value, out bytesConsumed);
+ return DecodeFromUtf8(source.Slice(index), out value, out bytesConsumed);
}
// If we got to this point, the final byte was a UTF-8 continuation byte.
@@ -591,7 +591,7 @@ namespace System.Text
for (int i = 3; i > 0; i--)
{
index--;
- if ((uint)index >= (uint)utf8Source.Length)
+ if ((uint)index >= (uint)source.Length)
{
goto Invalid; // out of data
}
@@ -600,7 +600,7 @@ namespace System.Text
// (bits 0xC0 set, values C0..FF). In two's complement this is the range [-64..127].
// It's just a fast way for us to terminate the search.
- if ((sbyte)utf8Source[index] >= -64)
+ if ((sbyte)source[index] >= -64)
{
goto ForwardDecode;
}
@@ -628,11 +628,11 @@ namespace System.Text
// fine since it'll be handled by the forward read. From this position, we'll perform a forward read
// and see if we consumed the entirety of the buffer.
- utf8Source = utf8Source.Slice(index);
- Debug.Assert(!utf8Source.IsEmpty, "Shouldn't reach this for empty inputs.");
+ source = source.Slice(index);
+ Debug.Assert(!source.IsEmpty, "Shouldn't reach this for empty inputs.");
- OperationStatus operationStatus = DecodeUtf8(utf8Source, out Rune tempRune, out int tempBytesConsumed);
- if (tempBytesConsumed == utf8Source.Length)
+ OperationStatus operationStatus = DecodeFromUtf8(source, out Rune tempRune, out int tempBytesConsumed);
+ if (tempBytesConsumed == source.Length)
{
// If this forward read consumed the entirety of the end of the input buffer, we can return it
// as the result of this function. It could be well-formed, incomplete, or invalid. If it's
@@ -662,15 +662,82 @@ namespace System.Text
}
}
- // returns the number of chars written
- private int EncodeToUtf16(Span<char> destination)
+ public static OperationStatus DecodeUtf16(ReadOnlySpan<char> utf16Source, out Rune result, out int charsConsumed)
{
- Debug.Assert(destination.Length >= Utf16SequenceLength, "Caller should've provided a large enough buffer.");
- bool success = TryEncode(destination, out int charsWritten);
- Debug.Assert(success, "TryEncode should never fail given a large enough buffer.");
+ // [TODO] This method was renamed to DecodeFromUtf16. We'll leave this copy of
+ // the method here temporarily so that we don't break corefx consumers
+ // while the rename takes place.
+ // Tracking issue: https://github.com/dotnet/coreclr/issues/23319
+
+ return DecodeFromUtf16(utf16Source, out result, out charsConsumed);
+ }
+
+ public static OperationStatus DecodeUtf16FromEnd(ReadOnlySpan<char> utf16Source, out Rune result, out int charsConsumed)
+ {
+ // [TODO] This method was renamed to DecodeLastFromUtf16. We'll leave this copy of
+ // the method here temporarily so that we don't break corefx consumers
+ // while the rename takes place.
+ // Tracking issue: https://github.com/dotnet/coreclr/issues/23319
+
+ return DecodeLastFromUtf16(utf16Source, out result, out charsConsumed);
+ }
+
+ public static OperationStatus DecodeUtf8(ReadOnlySpan<byte> utf8Source, out Rune result, out int bytesConsumed)
+ {
+ // [TODO] This method was renamed to DecodeFromUtf8. We'll leave this copy of
+ // the method here temporarily so that we don't break corefx consumers
+ // while the rename takes place.
+ // Tracking issue: https://github.com/dotnet/coreclr/issues/23319
+
+ return DecodeFromUtf8(utf8Source, out result, out bytesConsumed);
+ }
+
+ public static OperationStatus DecodeUtf8FromEnd(ReadOnlySpan<byte> utf8Source, out Rune result, out int bytesConsumed)
+ {
+ // [TODO] This method was renamed to DecodeLastFromUtf8. We'll leave this copy of
+ // the method here temporarily so that we don't break corefx consumers
+ // while the rename takes place.
+ // Tracking issue: https://github.com/dotnet/coreclr/issues/23319
+
+ return DecodeLastFromUtf8(utf8Source, out result, out bytesConsumed);
+ }
+
+ /// <summary>
+ /// Encodes this <see cref="Rune"/> to a UTF-16 destination buffer.
+ /// </summary>
+ /// <param name="destination">The buffer to which to write this value as UTF-16.</param>
+ /// <returns>The number of <see cref="char"/>s written to <paramref name="destination"/>.</returns>
+ /// <exception cref="ArgumentException">
+ /// If <paramref name="destination"/> is not large enough to hold the output.
+ /// </exception>
+ public int EncodeToUtf16(Span<char> destination)
+ {
+ if (!TryEncodeToUtf16(destination, out int charsWritten))
+ {
+ ThrowHelper.ThrowArgumentException_DestinationTooShort();
+ }
+
return charsWritten;
}
+ /// <summary>
+ /// Encodes this <see cref="Rune"/> to a UTF-8 destination buffer.
+ /// </summary>
+ /// <param name="destination">The buffer to which to write this value as UTF-8.</param>
+ /// <returns>The number of <see cref="byte"/>s written to <paramref name="destination"/>.</returns>
+ /// <exception cref="ArgumentException">
+ /// If <paramref name="destination"/> is not large enough to hold the output.
+ /// </exception>
+ public int EncodeToUtf8(Span<byte> destination)
+ {
+ if (!TryEncodeToUtf8(destination, out int bytesWritten))
+ {
+ ThrowHelper.ThrowArgumentException_DestinationTooShort();
+ }
+
+ return bytesWritten;
+ }
+
public override bool Equals(object obj) => (obj is Rune other) && this.Equals(other);
public bool Equals(Rune other) => (this == other);
@@ -893,7 +960,7 @@ namespace System.Text
/// The <see cref="Utf16SequenceLength"/> property can be queried ahead of time to determine
/// the required size of the <paramref name="destination"/> buffer.
/// </remarks>
- public bool TryEncode(Span<char> destination, out int charsWritten)
+ public bool TryEncodeToUtf16(Span<char> destination, out int charsWritten)
{
if (destination.Length >= 1)
{
@@ -917,6 +984,16 @@ namespace System.Text
return false;
}
+ public bool TryEncode(Span<char> destination, out int charsWritten)
+ {
+ // [TODO] This method was renamed to TryEncodeToUtf16. We'll leave this copy of
+ // the method here temporarily so that we don't break corefx consumers
+ // while the rename takes place.
+ // Tracking issue: https://github.com/dotnet/coreclr/issues/23319
+
+ return TryEncodeToUtf16(destination, out charsWritten);
+ }
+
/// <summary>
/// Encodes this <see cref="Rune"/> to a destination buffer as UTF-8 bytes.
/// </summary>
@@ -929,10 +1006,8 @@ namespace System.Text
/// The <see cref="Utf8SequenceLength"/> property can be queried ahead of time to determine
/// the required size of the <paramref name="destination"/> buffer.
/// </remarks>
- public bool TryEncodeToUtf8Bytes(Span<byte> destination, out int bytesWritten)
+ public bool TryEncodeToUtf8(Span<byte> destination, out int bytesWritten)
{
- // TODO: Optimize some of these writes by using BMI2 instructions.
-
// The bit patterns below come from the Unicode Standard, Table 3-6.
if (destination.Length >= 1)
@@ -987,6 +1062,16 @@ namespace System.Text
return false;
}
+ public bool TryEncodeToUtf8Bytes(Span<byte> destination, out int bytesWritten)
+ {
+ // [TODO] This method was renamed to TryEncodeToUtf8. We'll leave this copy of
+ // the method here temporarily so that we don't break corefx consumers
+ // while the rename takes place.
+ // Tracking issue: https://github.com/dotnet/coreclr/issues/23319
+
+ return TryEncodeToUtf8(destination, out bytesWritten);
+ }
+
/// <summary>
/// Attempts to get the <see cref="Rune"/> which begins at index <paramref name="index"/> in
/// string <paramref name="input"/>.
diff --git a/src/System.Private.CoreLib/shared/System/Text/UTF8Encoding.cs b/src/System.Private.CoreLib/shared/System/Text/UTF8Encoding.cs
index 5e1b9ae9a3..aaac975ec8 100644
--- a/src/System.Private.CoreLib/shared/System/Text/UTF8Encoding.cs
+++ b/src/System.Private.CoreLib/shared/System/Text/UTF8Encoding.cs
@@ -5,7 +5,7 @@
// The worker functions in this file was optimized for performance. If you make changes
// you should use care to consider all of the interesting cases.
-// The code of all worker functions in this file is written twice: Once as as a slow loop, and the
+// The code of all worker functions in this file is written twice: Once as a slow loop, and the
// second time as a fast loop. The slow loops handles all special cases, throws exceptions, etc.
// The fast loops attempts to blaze through as fast as possible with optimistic range checks,
// processing multiple characters at a time, and falling back to the slow loop for all special cases.
diff --git a/src/System.Private.CoreLib/shared/System/Text/Unicode/Utf8.cs b/src/System.Private.CoreLib/shared/System/Text/Unicode/Utf8.cs
index 0b941920af..9e6f245c10 100644
--- a/src/System.Private.CoreLib/shared/System/Text/Unicode/Utf8.cs
+++ b/src/System.Private.CoreLib/shared/System/Text/Unicode/Utf8.cs
@@ -48,7 +48,7 @@ namespace System.Text.Unicode
while (!source.IsEmpty)
{
- status = Rune.DecodeUtf16(source, out Rune firstScalarValue, out int charsConsumed);
+ status = Rune.DecodeFromUtf16(source, out Rune firstScalarValue, out int charsConsumed);
switch (status)
{
@@ -86,7 +86,7 @@ namespace System.Text.Unicode
// Do so now, and only terminate the loop if we ran out of space
// in the destination buffer.
- if (firstScalarValue.TryEncodeToUtf8Bytes(destination, out int bytesWritten))
+ if (firstScalarValue.TryEncodeToUtf8(destination, out int bytesWritten))
{
source = source.Slice(charsConsumed); // don't use Rune.Utf8SequenceLength; we may have performed substitution
destination = destination.Slice(bytesWritten);
@@ -131,7 +131,7 @@ namespace System.Text.Unicode
while (!source.IsEmpty)
{
- status = Rune.DecodeUtf8(source, out Rune firstScalarValue, out int bytesConsumed);
+ status = Rune.DecodeFromUtf8(source, out Rune firstScalarValue, out int bytesConsumed);
switch (status)
{
@@ -169,7 +169,7 @@ namespace System.Text.Unicode
// Do so now, and only terminate the loop if we ran out of space
// in the destination buffer.
- if (firstScalarValue.TryEncode(destination, out int charsWritten))
+ if (firstScalarValue.TryEncodeToUtf16(destination, out int charsWritten))
{
source = source.Slice(bytesConsumed); // don't use Rune.Utf16SequenceLength; we may have performed substitution
destination = destination.Slice(charsWritten);
diff --git a/src/System.Private.CoreLib/shared/System/Text/Unicode/Utf8Utility.cs b/src/System.Private.CoreLib/shared/System/Text/Unicode/Utf8Utility.cs
new file mode 100644
index 0000000000..6ee9ca05a6
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Text/Unicode/Utf8Utility.cs
@@ -0,0 +1,106 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Buffers;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.CompilerServices;
+
+namespace System.Text.Unicode
+{
+ internal static class Utf8Utility
+ {
+ /// <summary>
+ /// The maximum number of bytes that can result from UTF-8 transcoding
+ /// any Unicode scalar value.
+ /// </summary>
+ internal const int MaxBytesPerScalar = 4;
+
+ /// <summary>
+ /// The UTF-8 representation of <see cref="UnicodeUtility.ReplacementChar"/>.
+ /// </summary>
+ private static ReadOnlySpan<byte> ReplacementCharSequence => new byte[] { 0xEF, 0xBF, 0xBD };
+
+ /// <summary>
+ /// Returns the byte index in <paramref name="utf8Data"/> where the first invalid UTF-8 sequence begins,
+ /// or -1 if the buffer contains no invalid sequences. Also outs the <paramref name="isAscii"/> parameter
+ /// stating whether all data observed (up to the first invalid sequence or the end of the buffer, whichever
+ /// comes first) is ASCII.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int GetIndexOfFirstInvalidUtf8Sequence(ReadOnlySpan<byte> utf8Data, out bool isAscii)
+ {
+ // TODO_UTF8STRING: Replace this with the faster drop-in replacement when it's available (coreclr #21948).
+
+ bool tempIsAscii = true;
+ int originalDataLength = utf8Data.Length;
+
+ while (!utf8Data.IsEmpty)
+ {
+ if (Rune.DecodeFromUtf8(utf8Data, out Rune result, out int bytesConsumed) != OperationStatus.Done)
+ {
+ break;
+ }
+
+ tempIsAscii &= result.IsAscii;
+ utf8Data = utf8Data.Slice(bytesConsumed);
+ }
+
+ isAscii = tempIsAscii;
+ return (utf8Data.IsEmpty) ? -1 : (originalDataLength - utf8Data.Length);
+ }
+
+#if FEATURE_UTF8STRING
+ /// <summary>
+ /// Returns <paramref name="value"/> if it is null or contains only well-formed UTF-8 data;
+ /// otherwises allocates a new <see cref="Utf8String"/> instance containing the same data as
+ /// <paramref name="value"/> but where all invalid UTF-8 sequences have been replaced
+ /// with U+FFD.
+ /// </summary>
+ public static Utf8String ValidateAndFixupUtf8String(Utf8String value)
+ {
+ if (Utf8String.IsNullOrEmpty(value))
+ {
+ return value;
+ }
+
+ ReadOnlySpan<byte> valueAsBytes = value.AsBytes();
+
+ int idxOfFirstInvalidData = GetIndexOfFirstInvalidUtf8Sequence(valueAsBytes, out _);
+ if (idxOfFirstInvalidData < 0)
+ {
+ return value;
+ }
+
+ // TODO_UTF8STRING: Replace this with the faster implementation once it's available.
+ // (The faster implementation is in the dev/utf8string_bak branch currently.)
+
+ MemoryStream memStream = new MemoryStream();
+ memStream.Write(valueAsBytes.Slice(0, idxOfFirstInvalidData));
+
+ valueAsBytes = valueAsBytes.Slice(idxOfFirstInvalidData);
+ do
+ {
+ if (Rune.DecodeFromUtf8(valueAsBytes, out _, out int bytesConsumed) == OperationStatus.Done)
+ {
+ // Valid scalar value - copy data as-is to MemoryStream
+ memStream.Write(valueAsBytes.Slice(0, bytesConsumed));
+ }
+ else
+ {
+ // Invalid scalar value - copy U+FFFD to MemoryStream
+ memStream.Write(ReplacementCharSequence);
+ }
+
+ valueAsBytes = valueAsBytes.Slice(bytesConsumed);
+ } while (!valueAsBytes.IsEmpty);
+
+ bool success = memStream.TryGetBuffer(out ArraySegment<byte> memStreamBuffer);
+ Debug.Assert(success, "Couldn't get underlying MemoryStream buffer.");
+
+ return Utf8String.DangerousCreateWithoutValidation(memStreamBuffer, assumeWellFormed: true);
+ }
+#endif // FEATURE_UTF8STRING
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Text/UnicodeUtility.cs b/src/System.Private.CoreLib/shared/System/Text/UnicodeUtility.cs
index 3aad29679d..065c938d81 100644
--- a/src/System.Private.CoreLib/shared/System/Text/UnicodeUtility.cs
+++ b/src/System.Private.CoreLib/shared/System/Text/UnicodeUtility.cs
@@ -11,7 +11,7 @@ namespace System.Text
/// <summary>
/// The Unicode replacement character U+FFFD.
/// </summary>
- public const uint ReplacementChar = 0xFFFDU;
+ public const uint ReplacementChar = 0xFFFD;
/// <summary>
/// Returns the Unicode plane (0 through 16, inclusive) which contains this code point.
diff --git a/src/System.Private.CoreLib/shared/System/Threading/SpinWait.cs b/src/System.Private.CoreLib/shared/System/Threading/SpinWait.cs
index 88b4b411c7..6fa99e15c1 100644
--- a/src/System.Private.CoreLib/shared/System/Threading/SpinWait.cs
+++ b/src/System.Private.CoreLib/shared/System/Threading/SpinWait.cs
@@ -223,7 +223,7 @@ namespace System.Threading
// since we expect most callers will eventually block anyway.
//
// Also, cap the maximum spin count to a value such that many thousands of CPU cycles would not be wasted doing
- // the equivalent of YieldProcessor(), as that that point SwitchToThread/Sleep(0) are more likely to be able to
+ // the equivalent of YieldProcessor(), as at that point SwitchToThread/Sleep(0) are more likely to be able to
// allow other useful work to run. Long YieldProcessor() loops can help to reduce contention, but Sleep(1) is
// usually better for that.
//
diff --git a/src/System.Private.CoreLib/shared/System/Threading/Tasks/Sources/ManualResetValueTaskSourceCore.cs b/src/System.Private.CoreLib/shared/System/Threading/Tasks/Sources/ManualResetValueTaskSourceCore.cs
index 3244e0ad64..797ed6ed8a 100644
--- a/src/System.Private.CoreLib/shared/System/Threading/Tasks/Sources/ManualResetValueTaskSourceCore.cs
+++ b/src/System.Private.CoreLib/shared/System/Threading/Tasks/Sources/ManualResetValueTaskSourceCore.cs
@@ -197,7 +197,7 @@ namespace System.Threading.Tasks.Sources
}
}
- /// <summary>Signals that that the operation has completed. Invoked after the result or error has been set.</summary>
+ /// <summary>Signals that the operation has completed. Invoked after the result or error has been set.</summary>
private void SignalCompletion()
{
if (_completed)
diff --git a/src/System.Private.CoreLib/shared/System/ThrowHelper.cs b/src/System.Private.CoreLib/shared/System/ThrowHelper.cs
index 06b3ce41a6..4185382497 100644
--- a/src/System.Private.CoreLib/shared/System/ThrowHelper.cs
+++ b/src/System.Private.CoreLib/shared/System/ThrowHelper.cs
@@ -68,7 +68,7 @@ namespace System
internal static void ThrowArgumentException_DestinationTooShort()
{
- throw new ArgumentException(SR.Argument_DestinationTooShort);
+ throw new ArgumentException(SR.Argument_DestinationTooShort, "destination");
}
internal static void ThrowArgumentException_OverlapAlignmentMismatch()
diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs b/src/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs
index 608d1579fa..d1c6aa9bb7 100644
--- a/src/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs
+++ b/src/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComActivator.cs
@@ -215,7 +215,7 @@ $@"{nameof(GetClassFactoryForTypeInternal)} arguments:
{
if (!s_AssemblyLoadContexts.TryGetValue(assemblyPath, out alc))
{
- alc = new ComServerLoadContext(assemblyPath);
+ alc = new IsolatedComponentLoadContext(assemblyPath);
s_AssemblyLoadContexts.Add(assemblyPath, alc);
}
}
@@ -223,38 +223,6 @@ $@"{nameof(GetClassFactoryForTypeInternal)} arguments:
return alc;
}
- private class ComServerLoadContext : AssemblyLoadContext
- {
- private readonly AssemblyDependencyResolver _resolver;
-
- public ComServerLoadContext(string comServerAssemblyPath)
- {
- _resolver = new AssemblyDependencyResolver(comServerAssemblyPath);
- }
-
- protected override Assembly Load(AssemblyName assemblyName)
- {
- string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
- if (assemblyPath != null)
- {
- return LoadFromAssemblyPath(assemblyPath);
- }
-
- return null;
- }
-
- protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
- {
- string libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
- if (libraryPath != null)
- {
- return LoadUnmanagedDllFromPath(libraryPath);
- }
-
- return IntPtr.Zero;
- }
- }
-
[ComVisible(true)]
private class BasicClassFactory : IClassFactory
{
diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs b/src/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs
new file mode 100644
index 0000000000..097f47a825
--- /dev/null
+++ b/src/System.Private.CoreLib/src/Internal/Runtime/InteropServices/InMemoryAssemblyLoader.cs
@@ -0,0 +1,32 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Runtime.Loader;
+
+namespace Internal.Runtime.InteropServices
+{
+ /// <summary>
+ /// This class enables the .NET IJW host to load an in-memory module as a .NET assembly
+ /// </summary>
+ public static class InMemoryAssemblyLoader
+ {
+ /// <summary>
+ /// Loads into an isolated AssemblyLoadContext an assembly that has already been loaded into memory by the OS loader as a native module.
+ /// </summary>
+ /// <param name="moduleHandle">The native module handle for the assembly.</param>
+ /// <param name="assemblyPath">The path to the assembly (as a pointer to a UTF-16 C string).</param>
+ public static unsafe void LoadInMemoryAssembly(IntPtr moduleHandle, IntPtr assemblyPath)
+ {
+ // We don't cache the ALCs here since each IJW assembly will call this method at most once
+ // (the load process rewrites the stubs that call here to call the actual methods they're supposed to)
+ AssemblyLoadContext context = new IsolatedComponentLoadContext(Marshal.PtrToStringUni(assemblyPath));
+ context.LoadFromInMemoryModule(moduleHandle);
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/InteropServices/IsolatedComponentLoadContext.cs b/src/System.Private.CoreLib/src/Internal/Runtime/InteropServices/IsolatedComponentLoadContext.cs
new file mode 100644
index 0000000000..d78d7dd51c
--- /dev/null
+++ b/src/System.Private.CoreLib/src/Internal/Runtime/InteropServices/IsolatedComponentLoadContext.cs
@@ -0,0 +1,47 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Reflection;
+using System.Runtime.Loader;
+
+namespace Internal.Runtime.InteropServices
+{
+ /// <summary>
+ /// An <see cref="IsolatedComponentLoadContext" /> is an AssemblyLoadContext that can be used to isolate components such as COM components
+ /// or IJW components loaded from native. It provides a load context that uses an <see cref="AssemblyDependencyResolver" /> to resolve the component's
+ /// dependencies within the ALC and not pollute the default ALC.
+ ///</summary>
+ internal class IsolatedComponentLoadContext : AssemblyLoadContext
+ {
+ private readonly AssemblyDependencyResolver _resolver;
+
+ public IsolatedComponentLoadContext(string componentAssemblyPath)
+ {
+ _resolver = new AssemblyDependencyResolver(componentAssemblyPath);
+ }
+
+ protected override Assembly Load(AssemblyName assemblyName)
+ {
+ string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
+ if (assemblyPath != null)
+ {
+ return LoadFromAssemblyPath(assemblyPath);
+ }
+
+ return null;
+ }
+
+ protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
+ {
+ string libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
+ if (libraryPath != null)
+ {
+ return LoadUnmanagedDllFromPath(libraryPath);
+ }
+
+ return IntPtr.Zero;
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/src/System/Char8.cs b/src/System.Private.CoreLib/src/System/Char8.cs
new file mode 100644
index 0000000000..7a71e2faa0
--- /dev/null
+++ b/src/System.Private.CoreLib/src/System/Char8.cs
@@ -0,0 +1,69 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System
+{
+ /// <summary>
+ /// Represents a UTF-8 code unit, the elemental type of <see cref="Utf8String"/>.
+ /// </summary>
+ public readonly struct Char8 : IComparable<Char8>, IEquatable<Char8>
+ {
+ private readonly byte _value;
+
+ private Char8(byte value)
+ {
+ _value = value;
+ }
+
+ public static bool operator ==(Char8 left, Char8 right) => left._value == right._value;
+ public static bool operator !=(Char8 left, Char8 right) => left._value != right._value;
+ public static bool operator <(Char8 left, Char8 right) => left._value < right._value;
+ public static bool operator <=(Char8 left, Char8 right) => left._value <= right._value;
+ public static bool operator >(Char8 left, Char8 right) => left._value > right._value;
+ public static bool operator >=(Char8 left, Char8 right) => left._value >= right._value;
+
+ // Operators from Utf8Char to <other primitives>
+ // TODO: Once C# gets support for checked operators, we should add those here.
+
+ public static implicit operator byte(Char8 value) => value._value;
+ [CLSCompliant(false)]
+ public static explicit operator sbyte(Char8 value) => (sbyte)value._value; // explicit because can integer overflow
+ public static explicit operator char(Char8 value) => (char)value._value; // explicit because don't want to encourage char conversion
+ public static implicit operator short(Char8 value) => value._value;
+ [CLSCompliant(false)]
+ public static implicit operator ushort(Char8 value) => value._value;
+ public static implicit operator int(Char8 value) => value._value;
+ [CLSCompliant(false)]
+ public static implicit operator uint(Char8 value) => value._value;
+ public static implicit operator long(Char8 value) => value._value;
+ [CLSCompliant(false)]
+ public static implicit operator ulong(Char8 value) => value._value;
+
+ // Operators from <other primitives> to Char8; most are explicit because narrowing conversions could be lossy
+ // TODO: Once C# gets support for checked operators, we should add those here.
+
+ public static implicit operator Char8(byte value) => new Char8(value);
+ [CLSCompliant(false)]
+ public static explicit operator Char8(sbyte value) => new Char8((byte)value);
+ public static explicit operator Char8(char value) => new Char8((byte)value);
+ public static explicit operator Char8(short value) => new Char8((byte)value);
+ [CLSCompliant(false)]
+ public static explicit operator Char8(ushort value) => new Char8((byte)value);
+ public static explicit operator Char8(int value) => new Char8((byte)value);
+ [CLSCompliant(false)]
+ public static explicit operator Char8(uint value) => new Char8((byte)value);
+ public static explicit operator Char8(long value) => new Char8((byte)value);
+ [CLSCompliant(false)]
+ public static explicit operator Char8(ulong value) => new Char8((byte)value);
+
+ public int CompareTo(Char8 other) => this._value.CompareTo(other._value);
+
+ public override bool Equals(object obj) => (obj is Char8 other) && (this == other);
+ public bool Equals(Char8 other) => this == other;
+
+ public override int GetHashCode() => _value;
+
+ public override string ToString() => _value.ToString("X2");
+ }
+}
diff --git a/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.cs b/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.cs
index 1a5de88bd1..9f05540610 100644
--- a/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.cs
+++ b/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/EventPipe.cs
@@ -196,9 +196,9 @@ namespace System.Diagnostics.Tracing
s_sessionID = EventPipeInternal.Enable(
configuration.OutputFile,
configuration.CircularBufferSizeInMB,
- configuration.ProfilerSamplingRateInNanoseconds,
+ (ulong)configuration.ProfilerSamplingRateInNanoseconds,
providers,
- providers.Length,
+ (uint)providers.Length,
configuration.MultiFileTraceLengthInSeconds);
}
@@ -214,7 +214,13 @@ namespace System.Diagnostics.Tracing
// These PInvokes are used by the configuration APIs to interact with EventPipe.
//
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
- internal static extern UInt64 Enable(string outputFile, uint circularBufferSizeInMB, long profilerSamplingRateInNanoseconds, EventPipeProviderConfiguration[] providers, int numProviders, ulong multiFileTraceLengthInSeconds);
+ internal static extern UInt64 Enable(
+ string outputFile,
+ uint circularBufferSizeInMB,
+ ulong profilerSamplingRateInNanoseconds,
+ EventPipeProviderConfiguration[] providers,
+ uint numProviders,
+ ulong multiFileTraceLengthInSeconds);
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
internal static extern void Disable(UInt64 sessionID);
diff --git a/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSource.cs b/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSource.cs
index 046d5f0dac..010c2ea629 100644
--- a/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSource.cs
+++ b/src/System.Private.CoreLib/src/System/Diagnostics/Eventing/RuntimeEventSource.cs
@@ -15,17 +15,11 @@ namespace System.Diagnostics.Tracing
internal sealed class RuntimeEventSource : EventSource
{
private static RuntimeEventSource s_RuntimeEventSource;
- private EventCounter[] _counters;
-
- private enum Counter {
- GCHeapSize,
- Gen0GCCount,
- Gen1GCCount,
- Gen2GCCount,
- ExceptionCount
- }
-
- private Timer _timer;
+ private PollingCounter _gcHeapSizeCounter;
+ private IncrementingPollingCounter _gen0GCCounter;
+ private IncrementingPollingCounter _gen1GCCounter;
+ private IncrementingPollingCounter _gen2GCCounter;
+ private IncrementingPollingCounter _exceptionCounter;
private const int EnabledPollingIntervalMilliseconds = 1000; // 1 second
@@ -36,84 +30,23 @@ namespace System.Diagnostics.Tracing
private RuntimeEventSource(): base(new Guid(0x49592C0F, 0x5A05, 0x516D, 0xAA, 0x4B, 0xA6, 0x4E, 0x02, 0x02, 0x6C, 0x89), "System.Runtime", EventSourceSettings.EtwSelfDescribingEventFormat)
{
-
}
protected override void OnEventCommand(System.Diagnostics.Tracing.EventCommandEventArgs command)
{
if (command.Command == EventCommand.Enable)
{
- if (_counters == null)
- {
- // NOTE: These counters will NOT be disposed on disable command because we may be introducing
- // a race condition by doing that. We still want to create these lazily so that we aren't adding
- // overhead by at all times even when counters aren't enabled.
- _counters = new EventCounter[] {
- // TODO: process info counters
-
- // GC info counters
- new EventCounter("Total Memory by GC", this),
- new EventCounter("Gen 0 GC Count", this),
- new EventCounter("Gen 1 GC Count", this),
- new EventCounter("Gen 2 GC Count", this),
-
- new EventCounter("Exception Count", this)
- };
- }
-
-
- // Initialize the timer, but don't set it to run.
- // The timer will be set to run each time PollForTracingCommand is called.
-
- // TODO: We should not need this timer once we are done settling upon a high-level design for
- // what EventCounter is capable of doing. Once that decision is made, we should be able to
- // get rid of this.
- if (_timer == null)
- {
- _timer = new Timer(
- callback: new TimerCallback(PollForCounterUpdate),
- state: null,
- dueTime: Timeout.Infinite,
- period: Timeout.Infinite,
- flowExecutionContext: false);
- }
- // Trigger the first poll operation on when this EventSource is enabled
- PollForCounterUpdate(null);
- }
- else if (command.Command == EventCommand.Disable)
- {
- if (_timer != null)
- {
- _timer.Change(Timeout.Infinite, Timeout.Infinite); // disable the timer from running until System.Runtime is re-enabled
- }
- }
- }
-
- public void UpdateAllCounters()
- {
- // GC counters
- _counters[(int)Counter.GCHeapSize].WriteMetric(GC.GetTotalMemory(false));
- _counters[(int)Counter.Gen0GCCount].WriteMetric(GC.CollectionCount(0));
- _counters[(int)Counter.Gen1GCCount].WriteMetric(GC.CollectionCount(1));
- _counters[(int)Counter.Gen2GCCount].WriteMetric(GC.CollectionCount(2));
- _counters[(int)Counter.ExceptionCount].WriteMetric(Exception.GetExceptionCount());
- }
-
- private void PollForCounterUpdate(object state)
- {
- // TODO: Need to confirm with vancem about how to do error-handling here.
- // This disables to timer from getting rescheduled to run, which may or may not be
- // what we eventually want.
-
- // Make sure that any transient errors don't cause the listener thread to exit.
- try
- {
- UpdateAllCounters();
-
- // Schedule the timer to run again.
- _timer.Change(EnabledPollingIntervalMilliseconds, Timeout.Infinite);
+ // NOTE: These counters will NOT be disposed on disable command because we may be introducing
+ // a race condition by doing that. We still want to create these lazily so that we aren't adding
+ // overhead by at all times even when counters aren't enabled.
+
+ // On disable, PollingCounters will stop polling for values so it should be fine to leave them around.
+ _gcHeapSizeCounter = _gcHeapSizeCounter ?? new PollingCounter("GC Heap Size", this, () => GC.GetTotalMemory(false));
+ _gen0GCCounter = _gen0GCCounter ?? new IncrementingPollingCounter("Gen 0 GC Count", this, () => GC.CollectionCount(0));
+ _gen1GCCounter = _gen1GCCounter ?? new IncrementingPollingCounter("Gen 1 GC Count", this, () => GC.CollectionCount(1));
+ _gen2GCCounter = _gen2GCCounter ?? new IncrementingPollingCounter("Gen 2 GC Count", this, () => GC.CollectionCount(2));
+ _exceptionCounter = _exceptionCounter ?? new IncrementingPollingCounter("Exception Count", this, () => Exception.GetExceptionCount());
}
- catch { }
}
}
}
diff --git a/src/System.Private.CoreLib/src/System/Enum.CoreCLR.cs b/src/System.Private.CoreLib/src/System/Enum.CoreCLR.cs
index 1c4e4d1648..c44bce1bba 100644
--- a/src/System.Private.CoreLib/src/System/Enum.CoreCLR.cs
+++ b/src/System.Private.CoreLib/src/System/Enum.CoreCLR.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -31,9 +32,24 @@ namespace System
[MethodImpl(MethodImplOptions.InternalCall)]
private extern bool InternalHasFlag(Enum flags);
- private static TypeValuesAndNames GetCachedValuesAndNames(RuntimeType enumType, bool getNames)
+ private class EnumInfo
{
- TypeValuesAndNames entry = enumType.GenericCache as TypeValuesAndNames;
+ public readonly bool HasFlagsAttribute;
+ public readonly ulong[] Values;
+ public readonly string[] Names;
+
+ // Each entry contains a list of sorted pair of enum field names and values, sorted by values
+ public EnumInfo(bool hasFlagsAttribute, ulong[] values, string[] names)
+ {
+ HasFlagsAttribute = hasFlagsAttribute;
+ Values = values;
+ Names = names;
+ }
+ }
+
+ private static EnumInfo GetEnumInfo(RuntimeType enumType, bool getNames = true)
+ {
+ EnumInfo entry = enumType.GenericCache as EnumInfo;
if (entry == null || (getNames && entry.Names == null))
{
@@ -44,15 +60,27 @@ namespace System
JitHelpers.GetObjectHandleOnStack(ref values),
JitHelpers.GetObjectHandleOnStack(ref names),
getNames);
- bool isFlags = enumType.IsDefined(typeof(FlagsAttribute), inherit: false);
+ bool hasFlagsAttribute = enumType.IsDefined(typeof(FlagsAttribute), inherit: false);
- entry = new TypeValuesAndNames(isFlags, values, names);
+ entry = new EnumInfo(hasFlagsAttribute, values, names);
enumType.GenericCache = entry;
}
return entry;
}
+ internal static ulong[] InternalGetValues(RuntimeType enumType)
+ {
+ // Get all of the values
+ return GetEnumInfo(enumType, false).Values;
+ }
+
+ internal static string[] InternalGetNames(RuntimeType enumType)
+ {
+ // Get all of the names
+ return GetEnumInfo(enumType, true).Names;
+ }
+
[Intrinsic]
public bool HasFlag(Enum flag)
{
@@ -99,6 +127,14 @@ namespace System
return enumType.GetEnumValues();
}
+ public static bool IsDefined(Type enumType, object value)
+ {
+ if (enumType == null)
+ throw new ArgumentNullException(nameof(enumType));
+
+ return enumType.IsEnumDefined(value);
+ }
+
private static RuntimeType ValidateRuntimeType(Type enumType)
{
if (enumType == null)
@@ -109,5 +145,36 @@ namespace System
throw new ArgumentException(SR.Arg_MustBeType, nameof(enumType));
return rtType;
}
+
+ public int CompareTo(object target)
+ {
+ const int retIncompatibleMethodTables = 2; // indicates that the method tables did not match
+ const int retInvalidEnumType = 3; // indicates that the enum was of an unknown/unsupported underlying type
+
+ if (this == null)
+ throw new NullReferenceException();
+
+ int ret = InternalCompareTo(this, target);
+
+ if (ret < retIncompatibleMethodTables)
+ {
+ // -1, 0 and 1 are the normal return codes
+ return ret;
+ }
+ else if (ret == retIncompatibleMethodTables)
+ {
+ Type thisType = this.GetType();
+ Type targetType = target.GetType();
+
+ throw new ArgumentException(SR.Format(SR.Arg_EnumAndObjectMustBeSameType, targetType, thisType));
+ }
+ else
+ {
+ // assert valid return code (3)
+ Debug.Assert(ret == retInvalidEnumType, "Enum.InternalCompareTo return code was invalid");
+
+ throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
+ }
+ }
}
}
diff --git a/src/System.Private.CoreLib/src/System/Exception.cs b/src/System.Private.CoreLib/src/System/Exception.CoreCLR.cs
index c3f9ed7ee7..d7228bdf32 100644
--- a/src/System.Private.CoreLib/src/System/Exception.cs
+++ b/src/System.Private.CoreLib/src/System/Exception.CoreCLR.cs
@@ -2,15 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-/*=============================================================================
-**
-**
-**
-** Purpose: The base class for all exceptional conditions.
-**
-**
-=============================================================================*/
-
using System.Collections;
using System.Diagnostics;
using System.Reflection;
@@ -20,60 +11,11 @@ using System.Runtime.Serialization;
namespace System
{
- [Serializable]
- [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
- public class Exception : ISerializable
+ public partial class Exception : ISerializable
{
- public Exception()
+ partial void RestoreRemoteStackTrace(SerializationInfo info, StreamingContext context)
{
- _message = null;
- _stackTrace = null;
- _dynamicMethods = null;
- HResult = HResults.COR_E_EXCEPTION;
- _xcode = _COMPlusExceptionCode;
- _xptrs = (IntPtr)0;
-
- // Initialize the WatsonBuckets to be null
- _watsonBuckets = null;
-
- // Initialize the watson bucketing IP
- _ipForWatsonBuckets = UIntPtr.Zero;
- }
-
- public Exception(string message)
- : this()
- {
- _message = message;
- }
-
- // Creates a new Exception. All derived classes should
- // provide this constructor.
- // Note: the stack trace is not started until the exception
- // is thrown
- //
- public Exception(string message, Exception innerException)
- : this()
- {
- _message = message;
- _innerException = innerException;
- }
-
- protected Exception(SerializationInfo info, StreamingContext context)
- {
- if (info == null)
- throw new ArgumentNullException(nameof(info));
-
- _className = info.GetString("ClassName"); // Do not rename (binary serialization)
- _message = info.GetString("Message"); // Do not rename (binary serialization)
- _data = (IDictionary)(info.GetValueNoThrow("Data", typeof(IDictionary))); // Do not rename (binary serialization)
- _innerException = (Exception)(info.GetValue("InnerException", typeof(Exception))); // Do not rename (binary serialization)
- _helpURL = info.GetString("HelpURL"); // Do not rename (binary serialization)
- _stackTraceString = info.GetString("StackTraceString"); // Do not rename (binary serialization)
_remoteStackTraceString = info.GetString("RemoteStackTraceString"); // Do not rename (binary serialization)
- _remoteStackIndex = info.GetInt32("RemoteStackIndex"); // Do not rename (binary serialization)
-
- HResult = info.GetInt32("HResult"); // Do not rename (binary serialization)
- _source = info.GetString("Source"); // Do not rename (binary serialization)
// Get the WatsonBuckets that were serialized - this is particularly
// done to support exceptions going across AD transitions.
@@ -83,10 +25,6 @@ namespace System
// get null.
_watsonBuckets = (object)info.GetValueNoThrow("WatsonBuckets", typeof(byte[])); // Do not rename (binary serialization)
-
- if (_className == null || HResult == 0)
- throw new SerializationException(SR.Serialization_InsufficientState);
-
// If we are constructing a new exception after a cross-appdomain call...
if (context.State == StreamingContextStates.CrossAppDomain)
{
@@ -106,38 +44,13 @@ namespace System
}
}
-
- public virtual string Message
- {
- get
- {
- if (_message == null)
- {
- if (_className == null)
- {
- _className = GetClassName();
- }
- return SR.Format(SR.Exception_WasThrown, _className);
- }
- else
- {
- return _message;
- }
- }
- }
-
- public virtual IDictionary Data
+ private IDictionary CreateDataContainer()
{
- get
- {
- if (_data == null)
- if (IsImmutableAgileException(this))
- _data = new EmptyReadOnlyDictionaryInternal();
- else
- _data = new ListDictionaryInternal();
+ if (IsImmutableAgileException(this))
+ return new EmptyReadOnlyDictionaryInternal();
+ else
+ return new ListDictionaryInternal();
- return _data;
- }
}
[MethodImplAttribute(MethodImplOptions.InternalCall)]
@@ -210,40 +123,6 @@ namespace System
}
#endif // FEATURE_COMINTEROP
- private string GetClassName()
- {
- // Will include namespace but not full instantiation and assembly name.
- if (_className == null)
- _className = GetType().ToString();
-
- return _className;
- }
-
- // Retrieves the lowest exception (inner most) for the given Exception.
- // This will traverse exceptions using the innerException property.
- //
- public virtual Exception GetBaseException()
- {
- Exception inner = InnerException;
- Exception back = this;
-
- while (inner != null)
- {
- back = inner;
- inner = inner.InnerException;
- }
-
- return back;
- }
-
- // Returns the inner exception contained in this exception
- //
- public Exception InnerException
- {
- get { return _innerException; }
- }
-
-
[MethodImplAttribute(MethodImplOptions.InternalCall)]
static private extern IRuntimeMethodInfo GetMethodFromStackTrace(object stackTrace);
@@ -320,136 +199,31 @@ namespace System
return new StackTrace(e, needFileInfo).ToString(System.Diagnostics.StackTrace.TraceFormat.Normal);
}
- // Sets the help link for this exception.
- // This should be in a URL/URN form, such as:
- // "file:///C:/Applications/Bazzal/help.html#ErrorNum42"
- // Changed to be a read-write String and not return an exception
- public virtual string HelpLink
- {
- get
- {
- return _helpURL;
- }
- set
- {
- _helpURL = value;
- }
- }
-
- public virtual string Source
+ private string CreateSourceName()
{
- get
+ StackTrace st = new StackTrace(this, fNeedFileInfo: false);
+ if (st.FrameCount > 0)
{
- if (_source == null)
- {
- StackTrace st = new StackTrace(this, fNeedFileInfo: false);
- if (st.FrameCount > 0)
- {
- StackFrame sf = st.GetFrame(0);
- MethodBase method = sf.GetMethod();
+ StackFrame sf = st.GetFrame(0);
+ MethodBase method = sf.GetMethod();
- Module module = method.Module;
+ Module module = method.Module;
- RuntimeModule rtModule = module as RuntimeModule;
-
- if (rtModule == null)
- {
- System.Reflection.Emit.ModuleBuilder moduleBuilder = module as System.Reflection.Emit.ModuleBuilder;
- if (moduleBuilder != null)
- rtModule = moduleBuilder.InternalModule;
- else
- throw new ArgumentException(SR.Argument_MustBeRuntimeReflectionObject);
- }
-
- _source = rtModule.GetRuntimeAssembly().GetSimpleName();
- }
- }
+ RuntimeModule rtModule = module as RuntimeModule;
- return _source;
- }
- set { _source = value; }
- }
-
- public override string ToString()
- {
- return ToString(true, true);
- }
-
- private string ToString(bool needFileLineInfo, bool needMessage)
- {
- string message = (needMessage ? Message : null);
- string s;
-
- if (message == null || message.Length <= 0)
- {
- s = GetClassName();
- }
- else
- {
- s = GetClassName() + ": " + message;
- }
-
- if (_innerException != null)
- {
- s = s + " ---> " + _innerException.ToString(needFileLineInfo, needMessage) + Environment.NewLine +
- " " + SR.Exception_EndOfInnerExceptionStack;
- }
-
- string stackTrace = GetStackTrace(needFileLineInfo);
- if (stackTrace != null)
- {
- s += Environment.NewLine + stackTrace;
- }
-
- return s;
- }
-
- protected event EventHandler<SafeSerializationEventArgs> SerializeObjectState
- {
- add { throw new PlatformNotSupportedException(SR.PlatformNotSupported_SecureBinarySerialization); }
- remove { throw new PlatformNotSupportedException(SR.PlatformNotSupported_SecureBinarySerialization); }
- }
-
- public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
- {
- if (info == null)
- {
- throw new ArgumentNullException(nameof(info));
- }
-
- string tempStackTraceString = _stackTraceString;
-
- if (_stackTrace != null)
- {
- if (tempStackTraceString == null)
+ if (rtModule == null)
{
- tempStackTraceString = GetStackTrace(true, this);
- }
- if (_exceptionMethod == null)
- {
- _exceptionMethod = GetExceptionMethodFromStackTrace();
+ System.Reflection.Emit.ModuleBuilder moduleBuilder = module as System.Reflection.Emit.ModuleBuilder;
+ if (moduleBuilder != null)
+ rtModule = moduleBuilder.InternalModule;
+ else
+ throw new ArgumentException(SR.Argument_MustBeRuntimeReflectionObject);
}
- }
- if (_source == null)
- {
- _source = Source; // Set the Source information correctly before serialization
+ return rtModule.GetRuntimeAssembly().GetSimpleName();
}
- info.AddValue("ClassName", GetClassName(), typeof(string)); // Do not rename (binary serialization)
- info.AddValue("Message", _message, typeof(string)); // Do not rename (binary serialization)
- info.AddValue("Data", _data, typeof(IDictionary)); // Do not rename (binary serialization)
- info.AddValue("InnerException", _innerException, typeof(Exception)); // Do not rename (binary serialization)
- info.AddValue("HelpURL", _helpURL, typeof(string)); // Do not rename (binary serialization)
- info.AddValue("StackTraceString", tempStackTraceString, typeof(string)); // Do not rename (binary serialization)
- info.AddValue("RemoteStackTraceString", _remoteStackTraceString, typeof(string)); // Do not rename (binary serialization)
- info.AddValue("RemoteStackIndex", _remoteStackIndex, typeof(int)); // Do not rename (binary serialization)
- info.AddValue("ExceptionMethod", null, typeof(string)); // Do not rename (binary serialization)
- info.AddValue("HResult", HResult); // Do not rename (binary serialization)
- info.AddValue("Source", _source, typeof(string)); // Do not rename (binary serialization)
-
- // Serialize the Watson bucket details as well
- info.AddValue("WatsonBuckets", _watsonBuckets, typeof(byte[])); // Do not rename (binary serialization)
+ return null;
}
// This method will clear the _stackTrace of the exception object upon deserialization
@@ -604,18 +378,15 @@ namespace System
}
}
- private string _className; //Needed for serialization.
private MethodBase _exceptionMethod; //Needed for serialization.
internal string _message;
private IDictionary _data;
private Exception _innerException;
private string _helpURL;
private object _stackTrace;
- [OptionalField] // This isnt present in pre-V4 exception objects that would be serialized.
private object _watsonBuckets;
private string _stackTraceString; //Needed for serialization.
private string _remoteStackTraceString;
- private int _remoteStackIndex;
#pragma warning disable 414 // Field is not used from managed.
// _dynamicMethods is an array of System.Resolver objects, used to keep
// DynamicMethodDescs alive for the lifetime of the exception. We do this because
@@ -624,31 +395,15 @@ namespace System
private object _dynamicMethods;
#pragma warning restore 414
- // @MANAGED: HResult is used from within the EE! Rename with care - check VM directory
- private int _HResult; // HResult
-
- public int HResult
- {
- get
- {
- return _HResult;
- }
- set
- {
- _HResult = value;
- }
- }
-
- private string _source; // Mainly used by VB.
- // WARNING: Don't delete/rename _xptrs and _xcode - used by functions
- // on Marshal class. Native functions are in COMUtilNative.cpp & AppDomain
+ private string _source; // Mainly used by VB.
+ private UIntPtr _ipForWatsonBuckets; // Used to persist the IP for Watson Bucketing
private IntPtr _xptrs; // Internal EE stuff
#pragma warning disable 414 // Field is not used from managed.
- private int _xcode; // Internal EE stuff
+ private int _xcode = _COMPlusExceptionCode; // Internal EE stuff
#pragma warning restore 414
- [OptionalField]
- private UIntPtr _ipForWatsonBuckets; // Used to persist the IP for Watson Bucketing
+ // @MANAGED: HResult is used from within the EE! Rename with care - check VM directory
+ private int _HResult; // HResult
// See src\inc\corexcep.h's EXCEPTION_COMPLUS definition:
private const int _COMPlusExceptionCode = unchecked((int)0xe0434352); // Win32 exception code for COM+ exceptions
@@ -660,18 +415,30 @@ namespace System
return ToString(true, true);
}
- // this method is required so Object.GetType is not made virtual by the compiler
- // _Exception.GetType()
- public new Type GetType()
+ internal bool IsTransient
{
- return base.GetType();
+ get
+ {
+ return nIsTransient(HResult);
+ }
}
- internal bool IsTransient
+ private string SerializationRemoteStackTraceString => _remoteStackTraceString;
+
+ private object SerializationWatsonBuckets => _watsonBuckets;
+
+ private string SerializationStackTraceString
{
get
{
- return nIsTransient(HResult);
+ string stackTraceString = _stackTraceString;
+
+ if (stackTraceString == null && _stackTrace != null)
+ {
+ stackTraceString = GetStackTrace(true, this);
+ }
+
+ return stackTraceString;
}
}
diff --git a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
index 1851d9fb2b..74bb384486 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
+++ b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
@@ -147,7 +147,18 @@ namespace System.Runtime.CompilerServices
public static bool IsReferenceOrContainsReferences<T>()
{
// The body of this function will be replaced by the EE with unsafe code!!!
- // See getILIntrinsicImplementation for how this happens.
+ // See getILIntrinsicImplementationForRuntimeHelpers for how this happens.
+ throw new InvalidOperationException();
+ }
+
+ /// <returns>true if given type is bitwise equatable (memcmp can be used for equality checking)</returns>
+ /// <remarks>
+ /// Only use the result of this for Equals() comparison, not for CompareTo() comparison.
+ /// </remarks>
+ internal static bool IsBitwiseEquatable<T>()
+ {
+ // The body of this function will be replaced by the EE with unsafe code!!!
+ // See getILIntrinsicImplementationForRuntimeHelpers for how this happens.
throw new InvalidOperationException();
}
diff --git a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs
index 4cdf838fb1..e399ad6100 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs
+++ b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs
@@ -309,15 +309,6 @@ namespace System.Runtime.InteropServices
public static extern int GetHRForException(Exception e);
/// <summary>
- /// Converts the CLR exception to an HRESULT. This function also sets
- /// up an IErrorInfo for the exception.
- /// This function is only used in WinRT and converts ObjectDisposedException
- /// to RO_E_CLOSED
- /// </summary>
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern int GetHRForException_WinRT(Exception e);
-
- /// <summary>
/// Given a managed object that wraps an ITypeInfo, return its name.
/// </summary>
public static string GetTypeInfoName(ITypeInfo typeInfo)
@@ -743,21 +734,6 @@ namespace System.Runtime.InteropServices
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void ChangeWrapperHandleStrength(object otp, bool fIsWeak);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void InitializeWrapperForWinRT(object o, ref IntPtr pUnk);
-
-#if FEATURE_COMINTEROP_WINRT_MANAGED_ACTIVATION
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void InitializeManagedWinRTFactoryObject(object o, RuntimeType runtimeClassType);
-#endif
-
- /// <summary>
- /// Create activation factory and wraps it with a unique RCW.
- /// </summary>
- [MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern object GetNativeActivationFactory(Type type);
-
#endif // FEATURE_COMINTEROP
[MethodImpl(MethodImplOptions.InternalCall)]
diff --git a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeMarshal.cs b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeMarshal.cs
index 3199d2e239..5a151e7de2 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeMarshal.cs
+++ b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/WindowsRuntime/WindowsRuntimeMarshal.cs
@@ -1155,7 +1155,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
Marshal.QueryInterface(exceptionIUnknown, ref s_iidIErrorInfo, out exceptionIErrorInfo);
if (exceptionIErrorInfo != IntPtr.Zero)
{
- if (RoOriginateLanguageException(Marshal.GetHRForException_WinRT(e), e.Message, exceptionIErrorInfo))
+ if (RoOriginateLanguageException(Marshal.GetHRForException(e), e.Message, exceptionIErrorInfo))
{
IRestrictedErrorInfo restrictedError = UnsafeNativeMethods.GetRestrictedErrorInfo();
if (restrictedError != null)
@@ -1199,7 +1199,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
// If the type has any associated factory interfaces (i.e. supports non-default activation
// or has statics), the CCW for this instance of ManagedActivationFactory must support them.
- Marshal.InitializeManagedWinRTFactoryObject(activationFactory, (RuntimeType)type);
+ InitializeManagedWinRTFactoryObject(activationFactory, (RuntimeType)type);
return activationFactory;
}
@@ -1223,7 +1223,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime
if (type.IsWindowsRuntimeObject && type.IsImport)
{
- return (IActivationFactory)Marshal.GetNativeActivationFactory(type);
+ return (IActivationFactory)GetNativeActivationFactory(type);
}
else
{
@@ -1276,6 +1276,33 @@ namespace System.Runtime.InteropServices.WindowsRuntime
}
}
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public static extern object GetUniqueObjectForIUnknownWithoutUnboxing(IntPtr unknown);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal static extern void InitializeWrapper(object o, ref IntPtr pUnk);
+
+ /// <summary>
+ /// Converts the CLR exception to an HRESULT. This function also sets
+ /// up an IErrorInfo for the exception.
+ /// This function is only used in WinRT and converts ObjectDisposedException
+ /// to RO_E_CLOSED
+ /// </summary>
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal static extern int GetHRForException(Exception e);
+
+
+#if FEATURE_COMINTEROP_WINRT_MANAGED_ACTIVATION
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal static extern void InitializeManagedWinRTFactoryObject(object o, RuntimeType runtimeClassType);
+#endif
+
+ /// <summary>
+ /// Create activation factory and wraps it with a unique RCW.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal static extern object GetNativeActivationFactory(Type type);
+
[Conditional("_LOGGING")]
private static void Log(string s)
{
diff --git a/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CoreCLR.cs b/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CoreCLR.cs
index 60052d90ae..b36c382c38 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CoreCLR.cs
+++ b/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.CoreCLR.cs
@@ -52,6 +52,34 @@ namespace System.Runtime.Loader
return loadedAssembly;
}
+
+#if !FEATURE_PAL
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ private static extern IntPtr LoadFromInMemoryModuleInternal(IntPtr ptrNativeAssemblyLoadContext, IntPtr hModule, ObjectHandleOnStack retAssembly);
+
+
+ /// <summary>
+ /// Load a module that has already been loaded into memory by the OS loader as a .NET assembly.
+ /// </summary>
+ internal Assembly LoadFromInMemoryModule(IntPtr moduleHandle)
+ {
+ if (moduleHandle == IntPtr.Zero)
+ {
+ throw new ArgumentNullException(nameof(moduleHandle));
+ }
+ lock (_unloadLock)
+ {
+ VerifyIsAlive();
+
+ RuntimeAssembly loadedAssembly = null;
+ LoadFromInMemoryModuleInternal(
+ _nativeAssemblyLoadContext,
+ moduleHandle,
+ JitHelpers.GetObjectHandleOnStack(ref loadedAssembly));
+ return loadedAssembly;
+ }
+ }
+#endif
// This method is invoked by the VM when using the host-provided assembly load context
// implementation.
diff --git a/src/System.Private.CoreLib/src/System/Utf8Extensions.cs b/src/System.Private.CoreLib/src/System/Utf8Extensions.cs
new file mode 100644
index 0000000000..9fa2a54f16
--- /dev/null
+++ b/src/System.Private.CoreLib/src/System/Utf8Extensions.cs
@@ -0,0 +1,367 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Internal.Runtime.CompilerServices;
+
+namespace System
+{
+ public static class Utf8Extensions
+ {
+ /// <summary>
+ /// Projects <paramref name="text"/> as a <see cref="ReadOnlySpan{Byte}"/>.
+ /// </summary>
+ public static ReadOnlySpan<byte> AsBytes(this ReadOnlySpan<Char8> text)
+ {
+ return MemoryMarshal.Cast<Char8, byte>(text);
+ }
+
+ /// <summary>
+ /// Creates a new readonly span over the portion of the target <see cref="Utf8String"/>.
+ /// </summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlySpan<byte> AsBytes(this Utf8String text)
+ {
+ if (text == null)
+ return default;
+
+ return new ReadOnlySpan<byte>(ref text.DangerousGetMutableReference(), text.Length);
+ }
+
+ /// <summary>
+ /// Creates a new readonly span over the portion of the target <see cref="Utf8String"/>.
+ /// </summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <param name="start">The index at which to begin this slice.</param>
+ /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="text"/> is null.</exception>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;text.Length).
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlySpan<byte> AsBytes(this Utf8String text, int start)
+ {
+ if (text == null)
+ {
+ if (start != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+ return default;
+ }
+
+ if ((uint)start > (uint)text.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+
+ return new ReadOnlySpan<byte>(ref text.DangerousGetMutableReference(start), text.Length - start);
+ }
+
+ /// <summary>
+ /// Creates a new readonly span over the portion of the target <see cref="Utf8String"/>.
+ /// </summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <param name="start">The index at which to begin this slice.</param>
+ /// <param name="length">The desired length for the slice (exclusive).</param>
+ /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> index or <paramref name="length"/> is not in range.
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlySpan<byte> AsBytes(this Utf8String text, int start, int length)
+ {
+ if (text == null)
+ {
+ if (start != 0 || length != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+ return default;
+ }
+
+#if BIT64
+ // See comment in Span<T>.Slice for how this works.
+ if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)text.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+#else
+ if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+#endif
+
+ return new ReadOnlySpan<byte>(ref text.DangerousGetMutableReference(start), length);
+ }
+
+ /// <summary>
+ /// Creates a new readonly span over the portion of the target <see cref="Utf8String"/>.
+ /// </summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlySpan<Char8> AsSpan(this Utf8String text)
+ {
+ if (text == null)
+ return default;
+
+ return new ReadOnlySpan<Char8>(ref Unsafe.As<byte, Char8>(ref text.DangerousGetMutableReference()), text.Length);
+ }
+
+ /// <summary>
+ /// Creates a new readonly span over the portion of the target <see cref="Utf8String"/>.
+ /// </summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <param name="start">The index at which to begin this slice.</param>
+ /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="text"/> is null.</exception>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;text.Length).
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlySpan<Char8> AsSpan(this Utf8String text, int start)
+ {
+ if (text == null)
+ {
+ if (start != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+ return default;
+ }
+
+ if ((uint)start > (uint)text.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+
+ return new ReadOnlySpan<Char8>(ref Unsafe.As<byte, Char8>(ref text.DangerousGetMutableReference(start)), text.Length - start);
+ }
+
+ /// <summary>
+ /// Creates a new readonly span over the portion of the target <see cref="Utf8String"/>.
+ /// </summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <param name="start">The index at which to begin this slice.</param>
+ /// <param name="length">The desired length for the slice (exclusive).</param>
+ /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> index or <paramref name="length"/> is not in range.
+ /// </exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ReadOnlySpan<Char8> AsSpan(this Utf8String text, int start, int length)
+ {
+ if (text == null)
+ {
+ if (start != 0 || length != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+ return default;
+ }
+
+#if BIT64
+ // See comment in Span<T>.Slice for how this works.
+ if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)text.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+#else
+ if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+#endif
+
+ return new ReadOnlySpan<Char8>(ref Unsafe.As<byte, Char8>(ref text.DangerousGetMutableReference(start)), length);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target <see cref="Utf8String"/>.</summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
+ public static ReadOnlyMemory<Char8> AsMemory(this Utf8String text)
+ {
+ if (text == null)
+ return default;
+
+ return new ReadOnlyMemory<Char8>(text, 0, text.Length);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target <see cref="Utf8String"/>.</summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <param name="start">The index at which to begin this slice.</param>
+ /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;text.Length).
+ /// </exception>
+ public static ReadOnlyMemory<Char8> AsMemory(this Utf8String text, int start)
+ {
+ if (text == null)
+ {
+ if (start != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+ return default;
+ }
+
+ if ((uint)start > (uint)text.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+
+ return new ReadOnlyMemory<Char8>(text, start, text.Length - start);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target <see cref="Utf8String"/>.</summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <param name="startIndex">The index at which to begin this slice.</param>
+ public static ReadOnlyMemory<Char8> AsMemory(this Utf8String text, Index startIndex)
+ {
+ if (text == null)
+ {
+ if (!startIndex.Equals(Index.Start))
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
+
+ return default;
+ }
+
+ int actualIndex = startIndex.GetOffset(text.Length);
+ if ((uint)actualIndex > (uint)text.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ return new ReadOnlyMemory<Char8>(text, actualIndex, text.Length - actualIndex);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target <see cref="Utf8String"/>.</summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <param name="start">The index at which to begin this slice.</param>
+ /// <param name="length">The desired length for the slice (exclusive).</param>
+ /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> index or <paramref name="length"/> is not in range.
+ /// </exception>
+ public static ReadOnlyMemory<Char8> AsMemory(this Utf8String text, int start, int length)
+ {
+ if (text == null)
+ {
+ if (start != 0 || length != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+ return default;
+ }
+
+#if BIT64
+ // See comment in Span<T>.Slice for how this works.
+ if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)text.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+#else
+ if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+#endif
+
+ return new ReadOnlyMemory<Char8>(text, start, length);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target <see cref="Utf8String"/>.</summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <param name="range">The range used to indicate the start and length of the sliced string.</param>
+ public static ReadOnlyMemory<Char8> AsMemory(this Utf8String text, Range range)
+ {
+ if (text == null)
+ {
+ Index startIndex = range.Start;
+ Index endIndex = range.End;
+
+ if (!startIndex.Equals(Index.Start) || !endIndex.Equals(Index.Start))
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
+
+ return default;
+ }
+
+ (int start, int length) = range.GetOffsetAndLength(text.Length);
+ return new ReadOnlyMemory<Char8>(text, start, length);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target <see cref="Utf8String"/>.</summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
+ public static ReadOnlyMemory<byte> AsMemoryBytes(this Utf8String text)
+ {
+ if (text == null)
+ return default;
+
+ return new ReadOnlyMemory<byte>(text, 0, text.Length);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target <see cref="Utf8String"/>.</summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <param name="start">The index at which to begin this slice.</param>
+ /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;text.Length).
+ /// </exception>
+ public static ReadOnlyMemory<byte> AsMemoryBytes(this Utf8String text, int start)
+ {
+ if (text == null)
+ {
+ if (start != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+ return default;
+ }
+
+ if ((uint)start > (uint)text.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+
+ return new ReadOnlyMemory<byte>(text, start, text.Length - start);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target <see cref="Utf8String"/>.</summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <param name="startIndex">The index at which to begin this slice.</param>
+ public static ReadOnlyMemory<byte> AsMemoryBytes(this Utf8String text, Index startIndex)
+ {
+ if (text == null)
+ {
+ if (!startIndex.Equals(Index.Start))
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
+
+ return default;
+ }
+
+ int actualIndex = startIndex.GetOffset(text.Length);
+ if ((uint)actualIndex > (uint)text.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException();
+
+ return new ReadOnlyMemory<byte>(text, actualIndex, text.Length - actualIndex);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target <see cref="Utf8String"/>.</summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <param name="start">The index at which to begin this slice.</param>
+ /// <param name="length">The desired length for the slice (exclusive).</param>
+ /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
+ /// <exception cref="System.ArgumentOutOfRangeException">
+ /// Thrown when the specified <paramref name="start"/> index or <paramref name="length"/> is not in range.
+ /// </exception>
+ public static ReadOnlyMemory<byte> AsMemoryBytes(this Utf8String text, int start, int length)
+ {
+ if (text == null)
+ {
+ if (start != 0 || length != 0)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+ return default;
+ }
+
+#if BIT64
+ // See comment in Span<T>.Slice for how this works.
+ if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)text.Length)
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+#else
+ if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
+#endif
+
+ return new ReadOnlyMemory<byte>(text, start, length);
+ }
+
+ /// <summary>Creates a new <see cref="ReadOnlyMemory{T}"/> over the portion of the target <see cref="Utf8String"/>.</summary>
+ /// <param name="text">The target <see cref="Utf8String"/>.</param>
+ /// <param name="range">The range used to indicate the start and length of the sliced string.</param>
+ public static ReadOnlyMemory<byte> AsMemoryBytes(this Utf8String text, Range range)
+ {
+ if (text == null)
+ {
+ Index startIndex = range.Start;
+ Index endIndex = range.End;
+
+ if (!startIndex.Equals(Index.Start) || !endIndex.Equals(Index.Start))
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
+
+ return default;
+ }
+
+ (int start, int length) = range.GetOffsetAndLength(text.Length);
+ return new ReadOnlyMemory<byte>(text, start, length);
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/src/System/Utf8String.Construction.cs b/src/System.Private.CoreLib/src/System/Utf8String.Construction.cs
new file mode 100644
index 0000000000..9ecd44f3ae
--- /dev/null
+++ b/src/System.Private.CoreLib/src/System/Utf8String.Construction.cs
@@ -0,0 +1,223 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Text.Unicode;
+
+namespace System
+{
+ public sealed partial class Utf8String
+ {
+ /*
+ * CONSTRUCTORS
+ *
+ * Defining a new constructor for string-like types (like Utf8String) requires changes both
+ * to the managed code below and to the native VM code. See the comment at the top of
+ * src/vm/ecall.cpp for instructions on how to add new overloads.
+ *
+ * The default behavior of each ctor is to validate the input, replacing invalid sequences with the
+ * Unicode replacement character U+FFFD. The resulting Utf8String instance will be well-formed but
+ * might not have full fidelity with the input data. This behavior can be controlled by calling
+ * any of the Create instances and specifying a different action.
+ */
+
+ /// <summary>
+ /// Creates a <see cref="Utf8String"/> instance from existing UTF-8 data.
+ /// </summary>
+ /// <remarks>
+ /// The UTF-8 data in <paramref name="value"/> is validated for well-formedness upon construction.
+ /// Invalid code unit sequences are replaced with U+FFFD in the resulting <see cref="Utf8String"/>.
+ /// </remarks>
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public extern Utf8String(ReadOnlySpan<byte> value);
+
+#if PROJECTN
+ [DependencyReductionRoot]
+#endif
+#if !CORECLR
+ static
+#endif
+ private Utf8String Ctor(ReadOnlySpan<byte> value)
+ {
+ if (value.IsEmpty)
+ {
+ return Empty;
+ }
+
+ Utf8String newString = FastAllocate(value.Length);
+ Buffer.Memmove(ref newString.DangerousGetMutableReference(), ref MemoryMarshal.GetReference(value), (uint)value.Length);
+ return Utf8Utility.ValidateAndFixupUtf8String(newString);
+ }
+
+ /// <summary>
+ /// Creates a <see cref="Utf8String"/> instance from existing UTF-8 data.
+ /// </summary>
+ /// <remarks>
+ /// The UTF-8 data in <paramref name="value"/> is validated for well-formedness upon construction.
+ /// Invalid code unit sequences are replaced with U+FFFD in the resulting <see cref="Utf8String"/>.
+ /// </remarks>
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public extern Utf8String(byte[] value, int startIndex, int length);
+
+#if PROJECTN
+ [DependencyReductionRoot]
+#endif
+#if !CORECLR
+ static
+#endif
+ private Utf8String Ctor(byte[] value, int startIndex, int length) => Ctor(new ReadOnlySpan<byte>(value, startIndex, length));
+
+ /// <summary>
+ /// Creates a <see cref="Utf8String"/> instance from existing null-terminated UTF-8 data.
+ /// </summary>
+ /// <remarks>
+ /// The UTF-8 data in <paramref name="value"/> is validated for well-formedness upon construction.
+ /// Invalid code unit sequences are replaced with U+FFFD in the resulting <see cref="Utf8String"/>.
+ /// </remarks>
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ [CLSCompliant(false)]
+ public unsafe extern Utf8String(byte* value);
+
+#if PROJECTN
+ [DependencyReductionRoot]
+#endif
+#if !CORECLR
+ static
+#endif
+ private unsafe Utf8String Ctor(byte* value)
+ {
+ if (value == null)
+ {
+ return Empty;
+ }
+
+ return Ctor(new ReadOnlySpan<byte>(value, string.strlen(value)));
+ }
+
+ /// <summary>
+ /// Creates a <see cref="Utf8String"/> instance from existing UTF-16 data.
+ /// </summary>
+ /// <remarks>
+ /// The UTF-16 data in <paramref name="value"/> is validated for well-formedness upon construction.
+ /// Invalid code unit sequences are replaced with U+FFFD in the resulting <see cref="Utf8String"/>.
+ /// </remarks>
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public extern Utf8String(ReadOnlySpan<char> value);
+
+#if PROJECTN
+ [DependencyReductionRoot]
+#endif
+#if !CORECLR
+ static
+#endif
+ private Utf8String Ctor(ReadOnlySpan<char> value)
+ {
+ if (value.IsEmpty)
+ {
+ return Empty;
+ }
+
+ // TODO_UTF8STRING: Call into optimized transcoding routine when it's available.
+
+ Utf8String newString = FastAllocate(Encoding.UTF8.GetByteCount(value));
+ Encoding.UTF8.GetBytes(value, new Span<byte>(ref newString.DangerousGetMutableReference(), newString.Length));
+ return newString;
+ }
+
+ /// <summary>
+ /// Creates a <see cref="Utf8String"/> instance from existing UTF-16 data.
+ /// </summary>
+ /// <remarks>
+ /// The UTF-16 data in <paramref name="value"/> is validated for well-formedness upon construction.
+ /// Invalid code unit sequences are replaced with U+FFFD in the resulting <see cref="Utf8String"/>.
+ /// </remarks>
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public extern Utf8String(char[] value, int startIndex, int length);
+
+#if PROJECTN
+ [DependencyReductionRoot]
+#endif
+#if !CORECLR
+ static
+#endif
+ private Utf8String Ctor(char[] value, int startIndex, int length) => Ctor(new ReadOnlySpan<char>(value, startIndex, length));
+
+ /// <summary>
+ /// Creates a <see cref="Utf8String"/> instance from existing null-terminated UTF-16 data.
+ /// </summary>
+ /// <remarks>
+ /// The UTF-16 data in <paramref name="value"/> is validated for well-formedness upon construction.
+ /// Invalid code unit sequences are replaced with U+FFFD in the resulting <see cref="Utf8String"/>.
+ /// </remarks>
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ [CLSCompliant(false)]
+ public unsafe extern Utf8String(char* value);
+
+#if PROJECTN
+ [DependencyReductionRoot]
+#endif
+#if !CORECLR
+ static
+#endif
+ private unsafe Utf8String Ctor(char* value)
+ {
+ if (value == null)
+ {
+ return Empty;
+ }
+
+ return Ctor(new ReadOnlySpan<char>(value, string.wcslen(value)));
+ }
+
+ /// <summary>
+ /// Creates a <see cref="Utf8String"/> instance from existing UTF-16 data.
+ /// </summary>
+ /// <remarks>
+ /// The UTF-16 data in <paramref name="value"/> is validated for well-formedness upon construction.
+ /// Invalid code unit sequences are replaced with U+FFFD in the resulting <see cref="Utf8String"/>.
+ /// </remarks>
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ public extern Utf8String(string value);
+
+#if PROJECTN
+ [DependencyReductionRoot]
+#endif
+#if !CORECLR
+ static
+#endif
+ private Utf8String Ctor(string value) => Ctor(value.AsSpan());
+
+ /*
+ * HELPER METHODS
+ */
+
+ /// <summary>
+ /// Creates a <see cref="Utf8String"/> instance from existing data, bypassing validation.
+ /// Also allows the caller to set flags dictating various attributes of the data.
+ /// </summary>
+ internal static Utf8String DangerousCreateWithoutValidation(ReadOnlySpan<byte> utf8Data, bool assumeWellFormed = false, bool assumeAscii = false)
+ {
+ if (utf8Data.IsEmpty)
+ {
+ return Empty;
+ }
+
+ Utf8String newString = FastAllocate(utf8Data.Length);
+ utf8Data.CopyTo(new Span<byte>(ref newString.DangerousGetMutableReference(), newString.Length));
+ return newString;
+ }
+
+ /// <summary>
+ /// Creates a new zero-initialized instance of the specified length. Actual storage allocated is "length + 1" bytes
+ /// because instances are null-terminated.
+ /// </summary>
+ /// <remarks>
+ /// The implementation of this method checks its input argument for overflow.
+ /// </remarks>
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern Utf8String FastAllocate(int length);
+ }
+}
diff --git a/src/System.Private.CoreLib/src/System/Utf8String.Manipulation.cs b/src/System.Private.CoreLib/src/System/Utf8String.Manipulation.cs
new file mode 100644
index 0000000000..6e5209962f
--- /dev/null
+++ b/src/System.Private.CoreLib/src/System/Utf8String.Manipulation.cs
@@ -0,0 +1,109 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace System
+{
+ public sealed partial class Utf8String
+ {
+ /// <summary>
+ /// Substrings this <see cref="Utf8String"/> without bounds checking.
+ /// </summary>
+ private Utf8String InternalSubstring(int startIndex, int length)
+ {
+ Debug.Assert(startIndex >= 0, "StartIndex cannot be negative.");
+ Debug.Assert(startIndex <= this.Length, "StartIndex cannot point beyond the end of the string (except to the null terminator).");
+ Debug.Assert(length >= 0, "Length cannot be negative.");
+ Debug.Assert(startIndex + length <= this.Length, "StartIndex and Length cannot point beyond the end of the string.");
+
+ Debug.Assert(startIndex != 0 && startIndex != this.Length, "Caller should handle StartIndex boundary conditions.");
+ Debug.Assert(length != 0 && length != this.Length, "Caller should handle Length boundary conditions.");
+
+ Utf8String newString = FastAllocate(length);
+ Buffer.Memmove(ref newString.DangerousGetMutableReference(), ref this.DangerousGetMutableReference(startIndex), (uint)length);
+ return newString;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Utf8String Substring(Index startIndex)
+ {
+ int actualIndex = startIndex.GetOffset(Length);
+ return Substring(actualIndex);
+ }
+
+ public Utf8String Substring(int startIndex)
+ {
+ if ((uint)startIndex > (uint)this.Length)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex);
+ }
+
+ // Optimizations: since instances are immutable, we can return 'this' or the known
+ // Empty instance if the caller passed us a startIndex at the string boundary.
+
+ if (startIndex == 0)
+ {
+ return this;
+ }
+
+ if (startIndex == Length)
+ {
+ return Empty;
+ }
+
+ return InternalSubstring(startIndex, Length - startIndex);
+ }
+
+ public Utf8String Substring(int startIndex, int length)
+ {
+ ValidateStartIndexAndLength(startIndex, length);
+
+ // Optimizations: since instances are immutable, we can return 'this' or the known
+ // Empty instance if the caller passed us a startIndex at the string boundary.
+
+ if (length == 0)
+ {
+ return Empty;
+ }
+
+ if (length == this.Length)
+ {
+ return this;
+ }
+
+ return InternalSubstring(startIndex, length);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Utf8String Substring(Range range)
+ {
+ (int start, int length) = range.GetOffsetAndLength(Length);
+ return Substring(start, length);
+ }
+
+ [StackTraceHidden]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void ValidateStartIndexAndLength(int startIndex, int length)
+ {
+#if BIT64
+ // See comment in Span<T>.Slice for how this works.
+ if ((ulong)(uint)startIndex + (ulong)(uint)length > (ulong)(uint)this.Length)
+ ValidateStartIndexAndLength_Throw(startIndex, length);
+#else
+ if ((uint)startIndex > (uint)this.Length || (uint)length > (uint)(this.Length - startIndex))
+ ValidateStartIndexAndLength_Throw(startIndex, length);
+#endif
+ }
+
+ [StackTraceHidden]
+ private void ValidateStartIndexAndLength_Throw(int startIndex, int length)
+ {
+ throw new ArgumentOutOfRangeException(paramName: ((uint)startIndex > (uint)this.Length) ? nameof(startIndex) : nameof(length));
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/src/System/Utf8String.Searching.cs b/src/System.Private.CoreLib/src/System/Utf8String.Searching.cs
new file mode 100644
index 0000000000..0373cdd4fd
--- /dev/null
+++ b/src/System.Private.CoreLib/src/System/Utf8String.Searching.cs
@@ -0,0 +1,93 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Text.Unicode;
+
+namespace System
+{
+ public sealed partial class Utf8String
+ {
+ // Ordinal search
+ public bool Contains(char value)
+ {
+ return Rune.TryCreate(value, out Rune result) && Contains(result);
+ }
+
+ // Ordinal search
+ public bool Contains(Rune value)
+ {
+ // TODO_UTF8STRING: This should be split into two methods:
+ // One which operates on a single-byte (ASCII) search value,
+ // the other which operates on a multi-byte (non-ASCII) search value.
+
+ Span<byte> runeBytes = stackalloc byte[Utf8Utility.MaxBytesPerScalar];
+ int runeBytesWritten = value.EncodeToUtf8(runeBytes);
+
+ return SpanHelpers.IndexOf(
+ ref DangerousGetMutableReference(), Length,
+ ref MemoryMarshal.GetReference(runeBytes), runeBytesWritten) >= 0;
+ }
+
+ // Ordinal search
+ public bool EndsWith(char value)
+ {
+ return Rune.TryCreate(value, out Rune result) && EndsWith(result);
+ }
+
+ // Ordinal search
+ public bool EndsWith(Rune value)
+ {
+ // TODO_UTF8STRING: This should be split into two methods:
+ // One which operates on a single-byte (ASCII) search value,
+ // the other which operates on a multi-byte (non-ASCII) search value.
+
+ Span<byte> runeBytes = stackalloc byte[Utf8Utility.MaxBytesPerScalar];
+ int runeBytesWritten = value.EncodeToUtf8(runeBytes);
+
+ return this.AsBytes().EndsWith(runeBytes.Slice(0, runeBytesWritten));
+ }
+
+ // Ordinal search
+ public int IndexOf(char value)
+ {
+ return Rune.TryCreate(value, out Rune result) ? IndexOf(result) : -1;
+ }
+
+ // Ordinal search
+ public int IndexOf(Rune value)
+ {
+ // TODO_UTF8STRING: This should be split into two methods:
+ // One which operates on a single-byte (ASCII) search value,
+ // the other which operates on a multi-byte (non-ASCII) search value.
+
+ Span<byte> runeBytes = stackalloc byte[Utf8Utility.MaxBytesPerScalar];
+ int runeBytesWritten = value.EncodeToUtf8(runeBytes);
+
+ return SpanHelpers.IndexOf(
+ ref DangerousGetMutableReference(), Length,
+ ref MemoryMarshal.GetReference(runeBytes), runeBytesWritten);
+ }
+
+ // Ordinal search
+ public bool StartsWith(char value)
+ {
+ return Rune.TryCreate(value, out Rune result) && StartsWith(result);
+ }
+
+ // Ordinal search
+ public bool StartsWith(Rune value)
+ {
+ // TODO_UTF8STRING: This should be split into two methods:
+ // One which operates on a single-byte (ASCII) search value,
+ // the other which operates on a multi-byte (non-ASCII) search value.
+
+ Span<byte> runeBytes = stackalloc byte[Utf8Utility.MaxBytesPerScalar];
+ int runeBytesWritten = value.EncodeToUtf8(runeBytes);
+
+ return this.AsBytes().StartsWith(runeBytes.Slice(0, runeBytesWritten));
+ }
+ }
+}
diff --git a/src/System.Private.CoreLib/src/System/Utf8String.cs b/src/System.Private.CoreLib/src/System/Utf8String.cs
new file mode 100644
index 0000000000..1a4357a06f
--- /dev/null
+++ b/src/System.Private.CoreLib/src/System/Utf8String.cs
@@ -0,0 +1,252 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Text;
+using Internal.Runtime.CompilerServices;
+
+namespace System
+{
+ /// <summary>
+ /// Represents an immutable string of UTF-8 code units.
+ /// </summary>
+ public sealed partial class Utf8String : IEquatable<Utf8String>
+ {
+ /*
+ * STATIC FIELDS
+ */
+
+ public static readonly Utf8String Empty = FastAllocate(0);
+
+ /*
+ * INSTANCE FIELDS
+ * Do not reorder these fields. They must match the layout of Utf8StringObject in object.h.
+ */
+
+ private readonly int _length;
+ private readonly byte _firstByte;
+
+ /*
+ * OPERATORS
+ */
+
+ /// <summary>
+ /// Compares two <see cref="Utf8String"/> instances for equality using a <see cref="StringComparison.Ordinal"/> comparer.
+ /// </summary>
+ public static bool operator ==(Utf8String left, Utf8String right) => Equals(left, right);
+
+ /// <summary>
+ /// Compares two <see cref="Utf8String"/> instances for inequality using a <see cref="StringComparison.Ordinal"/> comparer.
+ /// </summary>
+ public static bool operator !=(Utf8String left, Utf8String right) => !Equals(left, right);
+
+ /// <summary>
+ /// Projects a <see cref="Utf8String"/> instance as a <see cref="ReadOnlySpan{Byte}"/>.
+ /// </summary>
+ public static explicit operator ReadOnlySpan<byte>(Utf8String value) => value.AsBytes();
+
+ /// <summary>
+ /// Projects a <see cref="Utf8String"/> instance as a <see cref="ReadOnlySpan{Char8}"/>.
+ /// </summary>
+ public static implicit operator ReadOnlySpan<Char8>(Utf8String value) => value.AsSpan();
+
+ /*
+ * INSTANCE PROPERTIES
+ */
+
+ /// <summary>
+ /// Returns the length (in UTF-8 code units) of this instance.
+ /// </summary>
+ public int Length => _length;
+
+ /*
+ * INSTANCE INDEXERS
+ */
+
+ /// <summary>
+ /// Gets the <see cref="Char8"/> at the specified position.
+ /// </summary>
+ public Char8 this[int index]
+ {
+ get
+ {
+ // Just like String, we don't allow indexing into the null terminator itself.
+
+ if ((uint)index >= (uint)Length)
+ {
+ ThrowHelper.ThrowArgumentOutOfRange_IndexException();
+ }
+
+ return Unsafe.Add(ref DangerousGetMutableReference(), index);
+ }
+ }
+
+ /// <summary>
+ /// Gets the <see cref="Char8"/> at the specified position.
+ /// </summary>
+ public Char8 this[Index index]
+ {
+ get
+ {
+ // Just like String, we don't allow indexing into the null terminator itself.
+
+ int actualIndex = index.GetOffset(Length);
+ return this[actualIndex];
+ }
+ }
+
+ /// <summary>
+ /// Gets a substring of this <see cref="Utf8String"/> based on the provided <paramref name="range"/>.
+ /// </summary>
+ public Utf8String this[Range range] => Substring(range);
+
+ /*
+ * METHODS
+ */
+
+ /// <summary>
+ /// Returns a <em>mutable</em> reference to the first byte of this <see cref="Utf8String"/>
+ /// (or the null terminator if the string is empty).
+ /// </summary>
+ /// <returns></returns>
+ internal ref byte DangerousGetMutableReference() => ref Unsafe.AsRef(in _firstByte);
+
+ /// <summary>
+ /// Returns a <em>mutable</em> reference to the element at index <paramref name="index"/>
+ /// of this <see cref="Utf8String"/> instance. The index is not bounds-checked.
+ /// </summary>
+ internal ref byte DangerousGetMutableReference(int index)
+ {
+ // Allow retrieving references to the null terminator.
+ Debug.Assert((uint)index <= (uint)Length, "Caller should've performed bounds checking.");
+
+ return ref Unsafe.Add(ref DangerousGetMutableReference(), index);
+ }
+
+ /// <summary>
+ /// Performs an equality comparison using a <see cref="StringComparison.Ordinal"/> comparer.
+ /// </summary>
+ public override bool Equals(object obj)
+ {
+ return obj is Utf8String other && this.Equals(other);
+ }
+
+ /// <summary>
+ /// Performs an equality comparison using a <see cref="StringComparison.Ordinal"/> comparer.
+ /// </summary>
+ public bool Equals(Utf8String value)
+ {
+ // First, a very quick check for referential equality.
+
+ if (ReferenceEquals(this, value))
+ {
+ return true;
+ }
+
+ // Otherwise, perform a simple bitwise equality check.
+
+ return !(value is null)
+ && this.Length == value.Length
+ && SpanHelpers.SequenceEqual(ref this.DangerousGetMutableReference(), ref value.DangerousGetMutableReference(), (uint)Length);
+ }
+
+ /// <summary>
+ /// Compares two <see cref="Utf8String"/> instances using a <see cref="StringComparison.Ordinal"/> comparer.
+ /// </summary>
+ public static bool Equals(Utf8String left, Utf8String right)
+ {
+ // First, a very quick check for referential equality.
+
+ if (ReferenceEquals(left, right))
+ {
+ return true;
+ }
+
+ // Otherwise, perform a simple bitwise equality check.
+
+ return !(left is null)
+ && !(right is null)
+ && left.Length == right.Length
+ && SpanHelpers.SequenceEqual(ref left.DangerousGetMutableReference(), ref right.DangerousGetMutableReference(), (uint)left.Length);
+ }
+
+ /// <summary>
+ /// Returns a hash code using a <see cref="StringComparison.Ordinal"/> comparison.
+ /// </summary>
+ public override int GetHashCode()
+ {
+ // TODO_UTF8STRING: Consider whether this should use a different seed than String.GetHashCode.
+
+ ulong seed = Marvin.DefaultSeed;
+ return Marvin.ComputeHash32(ref DangerousGetMutableReference(), _length /* in bytes */, (uint)seed, (uint)(seed >> 32));
+ }
+
+ /// <summary>
+ /// Gets an immutable reference that can be used in a <see langword="fixed"/> statement. The resulting
+ /// reference can be pinned and used as a null-terminated <em>LPCUTF8STR</em>.
+ /// </summary>
+ /// <remarks>
+ /// If this <see cref="Utf8String"/> instance is empty, returns a reference to the null terminator.
+ /// </remarks>
+ [EditorBrowsable(EditorBrowsableState.Never)] // for compiler use only
+ public ref readonly byte GetPinnableReference() => ref _firstByte;
+
+ /// <summary>
+ /// Returns <see langword="true"/> if <paramref name="value"/> is <see langword="null"/> or zero length;
+ /// <see langword="false"/> otherwise.
+ /// </summary>
+ public static bool IsNullOrEmpty(Utf8String value)
+ {
+ // Copied from String.IsNullOrEmpty. See that method for detailed comments on why this pattern is used.
+ return (value is null || 0u >= (uint)value.Length) ? true : false;
+ }
+
+ /// <summary>
+ /// Returns the entire <see cref="Utf8String"/> as an array of bytes.
+ /// </summary>
+ public byte[] ToByteArray()
+ {
+ if (Length == 0)
+ {
+ return Array.Empty<byte>();
+ }
+
+ byte[] bytes = new byte[Length];
+ Buffer.Memmove(ref bytes.GetRawSzArrayData(), ref DangerousGetMutableReference(), (uint)Length);
+ return bytes;
+ }
+
+ /// <summary>
+ /// Returns a substring of this <see cref="Utf8String"/> as an array of bytes.
+ /// </summary>
+ public byte[] ToByteArray(int startIndex, int length)
+ {
+ ValidateStartIndexAndLength(startIndex, length);
+
+ if (length == 0)
+ {
+ return Array.Empty<byte>();
+ }
+
+ byte[] bytes = new byte[length];
+ Buffer.Memmove(ref bytes.GetRawSzArrayData(), ref DangerousGetMutableReference(startIndex), (uint)length);
+ return bytes;
+ }
+
+ /// <summary>
+ /// Converts this <see cref="Utf8String"/> instance to a <see cref="string"/>.
+ /// </summary>
+ /// <remarks>
+ /// Invalid subsequences are replaced with U+FFFD during conversion.
+ /// </remarks>
+ public override string ToString()
+ {
+ // TODO_UTF8STRING: Call into optimized transcoding routine when it's available.
+
+ return Encoding.UTF8.GetString(new ReadOnlySpan<byte>(ref DangerousGetMutableReference(), Length));
+ }
+ }
+}
diff --git a/src/classlibnative/bcltype/objectnative.cpp b/src/classlibnative/bcltype/objectnative.cpp
index a90a37a692..64914d8807 100644
--- a/src/classlibnative/bcltype/objectnative.cpp
+++ b/src/classlibnative/bcltype/objectnative.cpp
@@ -253,6 +253,9 @@ FCIMPL1(Object*, ObjectNative::Clone, Object* pThisUNSAFE)
// assert that String has overloaded the Clone() method
_ASSERTE(pMT != g_pStringClass);
+#ifdef FEATURE_UTF8STRING
+ _ASSERTE(pMT != g_pUtf8StringClass);
+#endif // FEATURE_UTF8STRING
if (pMT->IsArray()) {
refClone = DupArrayForCloning((BASEARRAYREF)refThis);
diff --git a/src/debug/daccess/daccess.cpp b/src/debug/daccess/daccess.cpp
index a5e1387caf..237a9b67da 100644
--- a/src/debug/daccess/daccess.cpp
+++ b/src/debug/daccess/daccess.cpp
@@ -7554,8 +7554,8 @@ BOOL OutOfProcessExceptionEventGetProcessIdAndThreadId(HANDLE hProcess, HANDLE h
#ifdef FEATURE_PAL
// UNIXTODO: mikem 1/13/15 Need appropriate PAL functions for getting ids
- *pPId = (DWORD)hProcess;
- *pThreadId = (DWORD)hThread;
+ *pPId = (DWORD)(SIZE_T)hProcess;
+ *pThreadId = (DWORD)(SIZE_T)hThread;
#else
#if !defined(FEATURE_CORESYSTEM)
HMODULE hKernel32 = WszGetModuleHandle(W("kernel32.dll"));
diff --git a/src/debug/debug-pal/CMakeLists.txt b/src/debug/debug-pal/CMakeLists.txt
index 83e400c28f..ac0f97e468 100644
--- a/src/debug/debug-pal/CMakeLists.txt
+++ b/src/debug/debug-pal/CMakeLists.txt
@@ -5,12 +5,11 @@ include_directories(../../pal/inc)
add_definitions(-DPAL_STDCPP_COMPAT=1)
if(WIN32)
- #use static crt
- add_definitions(-MT)
add_definitions(-DWIN32_LEAN_AND_MEAN)
include_directories(../../inc) #needed for warning control
- set(TWO_WAY_PIPE_SOURCES
+ set(TWO_WAY_PIPE_SOURCES
+ win/diagnosticsipc.cpp
win/twowaypipe.cpp
win/processdescriptor.cpp
)
@@ -24,6 +23,7 @@ if(CLR_CMAKE_PLATFORM_UNIX)
add_definitions(-D_POSIX_C_SOURCE=200809L)
set(TWO_WAY_PIPE_SOURCES
+ unix/diagnosticsipc.cpp
unix/twowaypipe.cpp
unix/processdescriptor.cpp
)
diff --git a/src/debug/debug-pal/unix/diagnosticsipc.cpp b/src/debug/debug-pal/unix/diagnosticsipc.cpp
new file mode 100644
index 0000000000..d9e0e94c9f
--- /dev/null
+++ b/src/debug/debug-pal/unix/diagnosticsipc.cpp
@@ -0,0 +1,181 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include <pal.h>
+#include <pal_assert.h>
+#include <new>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include "diagnosticsipc.h"
+#include "processdescriptor.h"
+
+IpcStream::DiagnosticsIpc::DiagnosticsIpc(const int serverSocket, sockaddr_un *const pServerAddress) :
+ _serverSocket(serverSocket),
+ _pServerAddress(new (std::nothrow) sockaddr_un),
+ _isUnlinked(false)
+{
+ _ASSERTE(_pServerAddress != nullptr);
+ _ASSERTE(_serverSocket != -1);
+ _ASSERTE(pServerAddress != nullptr);
+
+ if (_pServerAddress == nullptr || pServerAddress == nullptr)
+ return;
+ memcpy(_pServerAddress, pServerAddress, sizeof(sockaddr_un));
+}
+
+IpcStream::DiagnosticsIpc::~DiagnosticsIpc()
+{
+ if (_serverSocket != -1)
+ {
+ const int fSuccessClose = ::close(_serverSocket);
+ _ASSERTE(fSuccessClose != -1); // TODO: Add error handling?
+
+ Unlink();
+
+ delete _pServerAddress;
+ }
+}
+
+IpcStream::DiagnosticsIpc *IpcStream::DiagnosticsIpc::Create(const char *const pIpcName, ErrorCallback callback)
+{
+ const int serverSocket = ::socket(AF_UNIX, SOCK_STREAM, 0);
+ if (serverSocket == -1)
+ {
+ if (callback != nullptr)
+ callback(strerror(errno), errno);
+ _ASSERTE(serverSocket != -1);
+ return nullptr;
+ }
+
+ sockaddr_un serverAddress{};
+ serverAddress.sun_family = AF_UNIX;
+ const ProcessDescriptor pd = ProcessDescriptor::FromCurrentProcess();
+ PAL_GetTransportName(
+ sizeof(serverAddress.sun_path),
+ serverAddress.sun_path,
+ pIpcName,
+ pd.m_Pid,
+ pd.m_ApplicationGroupId,
+ "socket");
+
+ const int fSuccessBind = ::bind(serverSocket, (sockaddr *)&serverAddress, sizeof(serverAddress));
+ if (fSuccessBind == -1)
+ {
+ if (callback != nullptr)
+ callback(strerror(errno), errno);
+ _ASSERTE(fSuccessBind != -1);
+
+ const int fSuccessClose = ::close(serverSocket);
+ _ASSERTE(fSuccessClose != -1);
+
+ return nullptr;
+ }
+
+ const int fSuccessfulListen = ::listen(serverSocket, /* backlog */ 255);
+ if (fSuccessfulListen == -1)
+ {
+ if (callback != nullptr)
+ callback(strerror(errno), errno);
+ _ASSERTE(fSuccessfulListen != -1);
+
+ const int fSuccessUnlink = ::unlink(serverAddress.sun_path);
+ _ASSERTE(fSuccessUnlink != -1);
+
+ const int fSuccessClose = ::close(serverSocket);
+ _ASSERTE(fSuccessClose != -1);
+
+ return nullptr;
+ }
+
+ return new IpcStream::DiagnosticsIpc(serverSocket, &serverAddress);
+}
+
+IpcStream *IpcStream::DiagnosticsIpc::Accept(ErrorCallback callback) const
+{
+ sockaddr_un from;
+ socklen_t fromlen = sizeof(from);
+ const int clientSocket = ::accept(_serverSocket, (sockaddr *)&from, &fromlen);
+ if (clientSocket == -1)
+ {
+ if (callback != nullptr)
+ callback(strerror(errno), errno);
+ return nullptr;
+ }
+
+ auto pIpcStream = new (std::nothrow) IpcStream(clientSocket);
+ if (pIpcStream == nullptr && callback != nullptr)
+ callback("Failed to allocate an IpcStream object.", 1);
+ return pIpcStream;
+}
+
+//! This helps remove the socket from the filesystem when the runtime exits.
+//! From: http://man7.org/linux/man-pages/man7/unix.7.html#NOTES
+//! Binding to a socket with a filename creates a socket in the
+//! filesystem that must be deleted by the caller when it is no longer
+//! needed (using unlink(2)). The usual UNIX close-behind semantics
+//! apply; the socket can be unlinked at any time and will be finally
+//! removed from the filesystem when the last reference to it is closed.
+void IpcStream::DiagnosticsIpc::Unlink(ErrorCallback callback)
+{
+ if (_isUnlinked)
+ return;
+ _isUnlinked = true;
+
+ const int fSuccessUnlink = ::unlink(_pServerAddress->sun_path);
+ if (fSuccessUnlink == -1)
+ {
+ if (callback != nullptr)
+ callback(strerror(errno), errno);
+ _ASSERTE(fSuccessUnlink == 0);
+ }
+}
+
+IpcStream::~IpcStream()
+{
+ if (_clientSocket != -1)
+ {
+ const int fSuccessClose = ::close(_clientSocket);
+ _ASSERTE(fSuccessClose != -1);
+ }
+}
+
+bool IpcStream::Read(void *lpBuffer, const uint32_t nBytesToRead, uint32_t &nBytesRead) const
+{
+ _ASSERTE(lpBuffer != nullptr);
+
+ const ssize_t ssize = ::recv(_clientSocket, lpBuffer, nBytesToRead, 0);
+ const bool fSuccess = ssize != -1;
+
+ if (!fSuccess)
+ {
+ // TODO: Add error handling.
+ }
+
+ nBytesRead = static_cast<uint32_t>(ssize);
+ return fSuccess;
+}
+
+bool IpcStream::Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const
+{
+ _ASSERTE(lpBuffer != nullptr);
+
+ const ssize_t ssize = ::send(_clientSocket, lpBuffer, nBytesToWrite, 0);
+ const bool fSuccess = ssize != -1;
+
+ if (!fSuccess)
+ {
+ // TODO: Add error handling.
+ }
+
+ nBytesWritten = static_cast<uint32_t>(ssize);
+ return fSuccess;
+}
+
+bool IpcStream::Flush() const
+{
+ // fsync - http://man7.org/linux/man-pages/man2/fsync.2.html ???
+ return true;
+}
diff --git a/src/debug/debug-pal/win/diagnosticsipc.cpp b/src/debug/debug-pal/win/diagnosticsipc.cpp
new file mode 100644
index 0000000000..46f212836b
--- /dev/null
+++ b/src/debug/debug-pal/win/diagnosticsipc.cpp
@@ -0,0 +1,146 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include <assert.h>
+#include <new>
+#include <stdio.h>
+#include "diagnosticsipc.h"
+
+IpcStream::DiagnosticsIpc::DiagnosticsIpc(const char(&namedPipeName)[MaxNamedPipeNameLength])
+{
+ memcpy(_pNamedPipeName, namedPipeName, sizeof(_pNamedPipeName));
+}
+
+IpcStream::DiagnosticsIpc::~DiagnosticsIpc()
+{
+}
+
+IpcStream::DiagnosticsIpc *IpcStream::DiagnosticsIpc::Create(const char *const pIpcName, ErrorCallback callback)
+{
+ assert(pIpcName != nullptr);
+ if (pIpcName == nullptr)
+ return nullptr;
+
+ char namedPipeName[MaxNamedPipeNameLength]{};
+ const int nCharactersWritten = sprintf_s(
+ namedPipeName,
+ sizeof(namedPipeName),
+ "\\\\.\\pipe\\%s-%d",
+ pIpcName,
+ ::GetCurrentProcessId());
+
+ if (nCharactersWritten == -1)
+ {
+ if (callback != nullptr)
+ callback("Failed to generate the named pipe name", nCharactersWritten);
+ assert(nCharactersWritten != -1);
+ return nullptr;
+ }
+
+ return new IpcStream::DiagnosticsIpc(namedPipeName);
+}
+
+IpcStream *IpcStream::DiagnosticsIpc::Accept(ErrorCallback callback) const
+{
+ const uint32_t nInBufferSize = 16 * 1024;
+ const uint32_t nOutBufferSize = 16 * 1024;
+ HANDLE hPipe = ::CreateNamedPipeA(
+ _pNamedPipeName, // pipe name
+ PIPE_ACCESS_DUPLEX/* | FILE_FLAG_OVERLAPPED*/, // read/write access
+ PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, // message type pipe, message-read and blocking mode
+ PIPE_UNLIMITED_INSTANCES, // max. instances
+ nOutBufferSize, // output buffer size
+ nInBufferSize, // input buffer size
+ 0, // default client time-out
+ NULL); // default security attribute
+
+ if (hPipe == INVALID_HANDLE_VALUE)
+ {
+ if (callback != nullptr)
+ callback("Failed to create an instance of a named pipe.", ::GetLastError());
+ return nullptr;
+ }
+
+ const BOOL fSuccess = ::ConnectNamedPipe(hPipe, NULL) != 0;
+ const DWORD errorCode = ::GetLastError();
+ if (!fSuccess && (errorCode != ERROR_PIPE_CONNECTED))
+ {
+ if (callback != nullptr)
+ callback("Failed to wait for a client process to connect.", errorCode);
+ return nullptr;
+ }
+
+ auto pIpcStream = new (std::nothrow) IpcStream(hPipe);
+ if (pIpcStream == nullptr && callback != nullptr)
+ callback("Failed to allocate an IpcStream object.", 1);
+ return pIpcStream;
+}
+
+void IpcStream::DiagnosticsIpc::Unlink(ErrorCallback callback)
+{
+}
+
+IpcStream::~IpcStream()
+{
+ if (_hPipe != INVALID_HANDLE_VALUE)
+ {
+ const BOOL fSuccessDisconnectNamedPipe = ::DisconnectNamedPipe(_hPipe);
+ assert(fSuccessDisconnectNamedPipe != 0);
+
+ const BOOL fSuccessCloseHandle = ::CloseHandle(_hPipe);
+ assert(CloseHandle != 0);
+ }
+}
+
+bool IpcStream::Read(void *lpBuffer, const uint32_t nBytesToRead, uint32_t &nBytesRead) const
+{
+ assert(lpBuffer != nullptr);
+
+ DWORD nNumberOfBytesRead = 0;
+ const bool fSuccess = ::ReadFile(
+ _hPipe, // handle to pipe
+ lpBuffer, // buffer to receive data
+ nBytesToRead, // size of buffer
+ &nNumberOfBytesRead, // number of bytes read
+ NULL) != 0; // not overlapped I/O
+
+ if (!fSuccess)
+ {
+ // TODO: Add error handling.
+ }
+
+ nBytesRead = static_cast<uint32_t>(nNumberOfBytesRead);
+ return fSuccess;
+}
+
+bool IpcStream::Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const
+{
+ assert(lpBuffer != nullptr);
+
+ DWORD nNumberOfBytesWritten = 0;
+ const bool fSuccess = ::WriteFile(
+ _hPipe, // handle to pipe
+ lpBuffer, // buffer to write from
+ nBytesToWrite, // number of bytes to write
+ &nNumberOfBytesWritten, // number of bytes written
+ NULL) != 0; // not overlapped I/O
+
+ if (!fSuccess)
+ {
+ // TODO: Add error handling.
+ }
+
+ nBytesWritten = static_cast<uint32_t>(nNumberOfBytesWritten);
+ return fSuccess;
+}
+
+bool IpcStream::Flush() const
+{
+ const bool fSuccess = ::FlushFileBuffers(_hPipe) != 0;
+ if (!fSuccess)
+ {
+ // TODO: Add error handling.
+ }
+ return fSuccess;
+}
diff --git a/src/debug/ee/debugger.cpp b/src/debug/ee/debugger.cpp
index d1eabb2d00..cb083c3214 100644
--- a/src/debug/ee/debugger.cpp
+++ b/src/debug/ee/debugger.cpp
@@ -11100,7 +11100,7 @@ bool Debugger::HandleIPCEvent(DebuggerIPCEvent * pEvent)
{
// Get the appdomain
IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager();
- ADIndex appDomainIndex = ADIndex(reinterpret_cast<DWORD>(mgr->GetHandleContext(objectHandle)));
+ ADIndex appDomainIndex = ADIndex((DWORD)(SIZE_T)(mgr->GetHandleContext(objectHandle)));
pAppDomain = SystemDomain::GetAppDomainAtIndex(appDomainIndex);
_ASSERTE(pAppDomain != NULL);
diff --git a/src/debug/inc/diagnosticsipc.h b/src/debug/inc/diagnosticsipc.h
new file mode 100644
index 0000000000..54277e1af9
--- /dev/null
+++ b/src/debug/inc/diagnosticsipc.h
@@ -0,0 +1,79 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __DIAGNOSTICS_IPC_H__
+#define __DIAGNOSTICS_IPC_H__
+
+#include <stdint.h>
+
+#ifdef FEATURE_PAL
+ struct sockaddr_un;
+#else
+ #include <Windows.h>
+#endif /* FEATURE_PAL */
+
+typedef void (*ErrorCallback)(const char *szMessage, uint32_t code);
+
+class IpcStream final
+{
+public:
+ ~IpcStream();
+ bool Read(void *lpBuffer, const uint32_t nBytesToRead, uint32_t &nBytesRead) const;
+ bool Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const;
+ bool Flush() const;
+
+ class DiagnosticsIpc final
+ {
+ public:
+ ~DiagnosticsIpc();
+
+ //! Creates an IPC object
+ static DiagnosticsIpc *Create(const char *const pIpcName, ErrorCallback callback = nullptr);
+
+ //! Enables the underlaying IPC implementation to accept connection.
+ IpcStream *Accept(ErrorCallback callback = nullptr) const;
+
+ //! Used to unlink the socket so it can be removed from the filesystem
+ //! when the last reference to it is closed.
+ void Unlink(ErrorCallback callback = nullptr);
+
+ private:
+
+#ifdef FEATURE_PAL
+ const int _serverSocket;
+ sockaddr_un *const _pServerAddress;
+ bool _isUnlinked = false;
+
+ DiagnosticsIpc(const int serverSocket, sockaddr_un *const pServerAddress);
+#else
+ static const uint32_t MaxNamedPipeNameLength = 256;
+ char _pNamedPipeName[MaxNamedPipeNameLength]; // https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createnamedpipea
+
+ DiagnosticsIpc(const char(&namedPipeName)[MaxNamedPipeNameLength]);
+#endif /* FEATURE_PAL */
+
+ DiagnosticsIpc() = delete;
+ DiagnosticsIpc(const DiagnosticsIpc &src) = delete;
+ DiagnosticsIpc(DiagnosticsIpc &&src) = delete;
+ DiagnosticsIpc &operator=(const DiagnosticsIpc &rhs) = delete;
+ DiagnosticsIpc &&operator=(DiagnosticsIpc &&rhs) = delete;
+ };
+
+private:
+#ifdef FEATURE_PAL
+ int _clientSocket = -1;
+ IpcStream(int clientSocket) : _clientSocket(clientSocket) {}
+#else
+ HANDLE _hPipe = INVALID_HANDLE_VALUE;
+ IpcStream(HANDLE hPipe) : _hPipe(hPipe) {}
+#endif /* FEATURE_PAL */
+
+ IpcStream() = delete;
+ IpcStream(const IpcStream &src) = delete;
+ IpcStream(IpcStream &&src) = delete;
+ IpcStream &operator=(const IpcStream &rhs) = delete;
+ IpcStream &&operator=(IpcStream &&rhs) = delete;
+};
+
+#endif // __DIAGNOSTICS_IPC_H__
diff --git a/src/dlls/mscorrc/mscorrc.rc b/src/dlls/mscorrc/mscorrc.rc
index 517c548bd7..8e272ad2af 100644
--- a/src/dlls/mscorrc/mscorrc.rc
+++ b/src/dlls/mscorrc/mscorrc.rc
@@ -557,6 +557,7 @@ BEGIN
IDS_EE_BADMARSHAL_DECIMALARRAY "Invalid managed/unmanaged type combination (Decimal[] must be paired with an ArraySubType of Struct or Currency)."
IDS_EE_BADMARSHAL_WINRT_MARSHAL_AS "Invalid managed/unmanaged type combination (Windows Runtime parameters and fields must not have a MarshalAs attribute set)."
IDS_EE_BADMARSHAL_WINRT_MISSING_GUID "Invalid managed/unmanaged type combination (Windows Runtime interfaces, classes and delegates must have a Guid or a default interface)."
+ IDS_EE_BADMARSHAL_WINRT_COPYCTOR "Windows Runtime marshaler does not support types with copy constructor."
IDS_EE_BADMARSHAL_WINRT_DELEGATE "Invalid managed/unmanaged type combination (Delegates must be Windows Runtime delegates)."
IDS_EE_BADMARSHAL_DEFAULTIFACE_NOT_WINRT_IFACE "The default interface must refer to a Windows Runtime interface with a GUID."
IDS_EE_BADMARSHAL_DEFAULTIFACE_NOT_SUBTYPE "The default interface must refer to an interface that is implemented by the type."
@@ -611,6 +612,7 @@ BEGIN
IDS_EE_BADMARSHAL_ASANYRESTRICTION "AsAny cannot be used on return types, ByRef parameters, ArrayWithOffset, or parameters passed from unmanaged to managed."
IDS_EE_BADMARSHAL_VBBYVALSTRRESTRICTION "VBByRefStr can only be used in combination with in/out, ByRef managed-to-unmanaged strings."
IDS_EE_BADMARSHAL_AWORESTRICTION "ArrayWithOffsets can only be marshaled as inout, non-ByRef, managed-to-unmanaged parameters."
+ IDS_EE_BADMARSHAL_COPYCTORRESTRICTION "Classes with copy-ctors can only be marshaled by value."
IDS_EE_BADMARSHAL_ARGITERATORRESTRICTION "ArgIterators cannot be marshaled ByRef."
IDS_EE_BADMARSHAL_HANDLEREFRESTRICTION "HandleRefs cannot be marshaled ByRef or from unmanaged to managed."
IDS_EE_BADMARSHAL_SAFEHANDLENATIVETOCOM "SafeHandles cannot be marshaled from unmanaged to managed."
diff --git a/src/dlls/mscorrc/resource.h b/src/dlls/mscorrc/resource.h
index cec39bbb30..a7d94874f4 100644
--- a/src/dlls/mscorrc/resource.h
+++ b/src/dlls/mscorrc/resource.h
@@ -718,3 +718,5 @@
#define IDS_EE_NDIRECT_GETPROCADDR_WIN_DLL 0x2644
#define IDS_EE_NDIRECT_GETPROCADDR_UNIX_SO 0x2645
#define IDS_EE_BADMARSHAL_STRING_OUT 0x2646
+#define IDS_EE_BADMARSHAL_COPYCTORRESTRICTION 0x2647
+#define IDS_EE_BADMARSHAL_WINRT_COPYCTOR 0x2648
diff --git a/src/gc/gc.cpp b/src/gc/gc.cpp
index 9c82c11536..2e5ea1b26b 100644
--- a/src/gc/gc.cpp
+++ b/src/gc/gc.cpp
@@ -5854,7 +5854,7 @@ void gc_mechanisms::init_mechanisms()
promotion = FALSE;//TRUE;
compaction = TRUE;
#ifdef FEATURE_LOH_COMPACTION
- loh_compaction = gc_heap::should_compact_loh();
+ loh_compaction = gc_heap::loh_compaction_requested();
#else
loh_compaction = FALSE;
#endif //FEATURE_LOH_COMPACTION
@@ -21372,10 +21372,10 @@ retry:
}
}
-BOOL gc_heap::should_compact_loh()
+BOOL gc_heap::loh_compaction_requested()
{
// If hard limit is specified GC will automatically decide if LOH needs to be compacted.
- return (heap_hard_limit || loh_compaction_always_p || (loh_compaction_mode != loh_compaction_default));
+ return (loh_compaction_always_p || (loh_compaction_mode != loh_compaction_default));
}
inline
@@ -21545,7 +21545,7 @@ BOOL gc_heap::plan_loh()
void gc_heap::compact_loh()
{
- assert (should_compact_loh());
+ assert (loh_compaction_requested() || heap_hard_limit);
generation* gen = large_object_generation;
heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
diff --git a/src/gc/gcpriv.h b/src/gc/gcpriv.h
index 8a7dfdf6b8..9f863e6bb0 100644
--- a/src/gc/gcpriv.h
+++ b/src/gc/gcpriv.h
@@ -2228,7 +2228,7 @@ protected:
BOOL loh_object_p (uint8_t* o);
PER_HEAP_ISOLATED
- BOOL should_compact_loh();
+ BOOL loh_compaction_requested();
// If the LOH compaction mode is just to compact once,
// we need to see if we should reset it back to not compact.
diff --git a/src/gc/sample/gcenv.ee.cpp b/src/gc/sample/gcenv.ee.cpp
index 9a4e9c9a74..687fd5624b 100644
--- a/src/gc/sample/gcenv.ee.cpp
+++ b/src/gc/sample/gcenv.ee.cpp
@@ -100,7 +100,11 @@ uint32_t CLREventStatic::Wait(uint32_t dwMilliseconds, bool bAlertable)
return result;
}
+#ifndef __GNUC__
__declspec(thread) Thread * pCurrentThread;
+#else // !__GNUC__
+thread_local Thread * pCurrentThread;
+#endif // !__GNUC__
Thread * GetThread()
{
diff --git a/src/gc/unix/cgroup.cpp b/src/gc/unix/cgroup.cpp
index b66323a3b9..88eb415474 100644
--- a/src/gc/unix/cgroup.cpp
+++ b/src/gc/unix/cgroup.cpp
@@ -434,10 +434,19 @@ void CleanupCGroup()
size_t GetRestrictedPhysicalMemoryLimit()
{
- size_t physical_memory_limit;
+ size_t physical_memory_limit = 0;
if (!CGroup::GetPhysicalMemoryLimit(&physical_memory_limit))
- physical_memory_limit = SIZE_T_MAX;
+ return 0;
+
+ // If there's no memory limit specified on the container this
+ // actually returns 0x7FFFFFFFFFFFF000 (2^63-1 rounded down to
+ // 4k which is a common page size). So we know we are not
+ // running in a memory restricted environment.
+ if (physical_memory_limit > 0x7FFFFFFF00000000)
+ {
+ return 0;
+ }
struct rlimit curr_rlimit;
size_t rlimit_soft_limit = (size_t)RLIM_INFINITY;
diff --git a/src/gc/unix/gcenv.unix.cpp b/src/gc/unix/gcenv.unix.cpp
index ed09e89f26..572fc3b12f 100644
--- a/src/gc/unix/gcenv.unix.cpp
+++ b/src/gc/unix/gcenv.unix.cpp
@@ -449,7 +449,7 @@ the processors enabled.
--*/
static uintptr_t GetFullAffinityMask(int cpuCount)
{
- if (cpuCount < sizeof(uintptr_t) * 8)
+ if ((size_t)cpuCount < sizeof(uintptr_t) * 8)
{
return ((uintptr_t)1 << cpuCount) - 1;
}
diff --git a/src/gc/windows/gcenv.windows.cpp b/src/gc/windows/gcenv.windows.cpp
index f032405c66..f7d069c874 100644
--- a/src/gc/windows/gcenv.windows.cpp
+++ b/src/gc/windows/gcenv.windows.cpp
@@ -286,16 +286,21 @@ static size_t GetRestrictedPhysicalMemoryLimit()
if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET) != 0)
job_workingset_limit = limit_info.BasicLimitInformation.MaximumWorkingSetSize;
- job_physical_memory_limit = min (job_memory_limit, job_process_memory_limit);
- job_physical_memory_limit = min (job_physical_memory_limit, job_workingset_limit);
+ if ((job_memory_limit != (size_t)UINTPTR_MAX) ||
+ (job_process_memory_limit != (size_t)UINTPTR_MAX) ||
+ (job_workingset_limit != (size_t)UINTPTR_MAX))
+ {
+ job_physical_memory_limit = min (job_memory_limit, job_process_memory_limit);
+ job_physical_memory_limit = min (job_physical_memory_limit, job_workingset_limit);
- MEMORYSTATUSEX ms;
- ::GetProcessMemoryLoad(&ms);
- total_virtual = ms.ullTotalVirtual;
- total_physical = ms.ullAvailPhys;
+ MEMORYSTATUSEX ms;
+ ::GetProcessMemoryLoad(&ms);
+ total_virtual = ms.ullTotalVirtual;
+ total_physical = ms.ullAvailPhys;
- // A sanity check in case someone set a larger limit than there is actual physical memory.
- job_physical_memory_limit = (size_t) min (job_physical_memory_limit, ms.ullTotalPhys);
+ // A sanity check in case someone set a larger limit than there is actual physical memory.
+ job_physical_memory_limit = (size_t) min (job_physical_memory_limit, ms.ullTotalPhys);
+ }
}
}
diff --git a/src/inc/dacvars.h b/src/inc/dacvars.h
index fc5be15590..cec6d74dd7 100644
--- a/src/inc/dacvars.h
+++ b/src/inc/dacvars.h
@@ -168,6 +168,9 @@ DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pObjectClass, ::g_pObjectClass
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pRuntimeTypeClass, ::g_pRuntimeTypeClass)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pCanonMethodTableClass, ::g_pCanonMethodTableClass)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pStringClass, ::g_pStringClass)
+#ifdef FEATURE_UTF8STRING
+DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pUtf8StringClass, ::g_pUtf8StringClass)
+#endif // FEATURE_UTF8STRING
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pArrayClass, ::g_pArrayClass)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pSZArrayHelperClass, ::g_pSZArrayHelperClass)
DEFINE_DACVAR(ULONG, UNKNOWN_POINTER_TYPE, dac__g_pNullableClass, ::g_pNullableClass)
diff --git a/src/inc/loglf.h b/src/inc/loglf.h
index e7fbd519d9..76a9897b53 100644
--- a/src/inc/loglf.h
+++ b/src/inc/loglf.h
@@ -16,7 +16,7 @@ DEFINE_LOG_FACILITY(LF_GCALLOC ,0x00000100)
DEFINE_LOG_FACILITY(LF_CORDB ,0x00000200)
DEFINE_LOG_FACILITY(LF_CLASSLOADER ,0x00000400)
DEFINE_LOG_FACILITY(LF_CORPROF ,0x00000800)
-DEFINE_LOG_FACILITY(LF_REMOTING ,0x00001000)
+DEFINE_LOG_FACILITY(LF_DIAGNOSTICS_PORT ,0x00001000)
DEFINE_LOG_FACILITY(LF_DBGALLOC ,0x00002000)
DEFINE_LOG_FACILITY(LF_EH ,0x00004000)
DEFINE_LOG_FACILITY(LF_ENC ,0x00008000)
diff --git a/src/inc/stresslog.h b/src/inc/stresslog.h
index dd8c029b0a..8f98f9a7b2 100644
--- a/src/inc/stresslog.h
+++ b/src/inc/stresslog.h
@@ -127,7 +127,7 @@
LOG((facility, level, msg, data1, data2, data3, data4, data5, data6, data7)); \
} while(0)
-#define STRESS_LOG_COND0(facility, level, msg) do { \
+#define STRESS_LOG_COND0(facility, level, cond, msg) do { \
if (StressLog::LogOn(facility, level) && (cond)) \
StressLog::LogMsg(level, facility, 0, msg); \
LOG((facility, level, msg)); \
diff --git a/src/jit/CMakeLists.txt b/src/jit/CMakeLists.txt
index 5b98b4f507..013b8974ed 100644
--- a/src/jit/CMakeLists.txt
+++ b/src/jit/CMakeLists.txt
@@ -4,6 +4,11 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
include_directories("./jitstd")
include_directories("../inc")
+if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+ add_compile_options(-fpermissive)
+ add_compile_options(-Wno-error)
+endif()
+
if (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64 OR (CLR_CMAKE_TARGET_ARCH_I386 AND NOT CLR_CMAKE_PLATFORM_UNIX))
add_definitions(-DFEATURE_SIMD)
add_definitions(-DFEATURE_HW_INTRINSICS)
diff --git a/src/jit/assertionprop.cpp b/src/jit/assertionprop.cpp
index 1ee56b22d6..3747b2edca 100644
--- a/src/jit/assertionprop.cpp
+++ b/src/jit/assertionprop.cpp
@@ -453,9 +453,6 @@ void Compiler::optAddCopies()
noway_assert(tree && op1 && tree->OperIs(GT_ASG) && (op1->gtOper == GT_LCL_VAR) &&
(op1->gtLclVarCommon.gtLclNum == lclNum));
- /* TODO-Review: BB_UNITY_WEIGHT is not the correct block weight */
- unsigned blockWeight = BB_UNITY_WEIGHT;
-
/* Assign the old expression into the new temp */
GenTree* newAsgn = gtNewTempAssign(copyLclNum, tree->gtOp.gtOp2);
@@ -1309,7 +1306,6 @@ AssertionIndex Compiler::optCreateAssertion(GenTree* op1,
{
unsigned lclNum = op1->gtLclVarCommon.gtLclNum;
noway_assert(lclNum < lvaCount);
- LclVarDsc* lclVar = &lvaTable[lclNum];
// If the local variable is not in SSA then bail
if (!lvaInSsa(lclNum))
@@ -2203,8 +2199,6 @@ AssertionIndex Compiler::optFindComplementary(AssertionIndex assertIndex)
return index;
}
- optAssertionKind complementaryAssertionKind =
- (inputAssertion->assertionKind == OAK_EQUAL) ? OAK_NOT_EQUAL : OAK_EQUAL;
for (AssertionIndex index = 1; index <= optAssertionCount; ++index)
{
// Make sure assertion kinds are complementary and op1, op2 kinds match.
@@ -2404,8 +2398,7 @@ GenTree* Compiler::optVNConstantPropOnTree(BasicBlock* block, GenTree* stmt, Gen
return nullptr;
}
- GenTree* newTree = tree;
- GenTree* sideEffList = nullptr;
+ GenTree* newTree = tree;
switch (vnStore->TypeOfVN(vnCns))
{
case TYP_FLOAT:
@@ -3540,8 +3533,6 @@ GenTree* Compiler::optAssertionProp_Ind(ASSERT_VALARG_TP assertions, GenTree* tr
return nullptr;
}
- unsigned lclNum = op1->gtLclVarCommon.gtLclNum;
-
#ifdef DEBUG
bool vnBased = false;
AssertionIndex index = NO_ASSERTION_INDEX;
@@ -4437,7 +4428,6 @@ private:
ASSERT_TP* mJumpDestOut;
ASSERT_TP* mJumpDestGen;
- Compiler* m_pCompiler;
BitVecTraits* apTraits;
public:
@@ -4446,7 +4436,6 @@ public:
, preMergeJumpDestOut(BitVecOps::UninitVal())
, mJumpDestOut(jumpDestOut)
, mJumpDestGen(jumpDestGen)
- , m_pCompiler(pCompiler)
, apTraits(pCompiler->apTraits)
{
}
diff --git a/src/jit/bitset.h b/src/jit/bitset.h
index a0192e62e8..bddc2ea532 100644
--- a/src/jit/bitset.h
+++ b/src/jit/bitset.h
@@ -30,7 +30,7 @@ public:
{
unsigned res = 0;
// We process "u" in 4-bit nibbles, hence the "*2" below.
- for (int i = 0; i < sizeof(T) * 2; i++)
+ for (unsigned int i = 0; i < sizeof(T) * 2; i++)
{
res += BitCountTable[u & 0xf];
u >>= 4;
diff --git a/src/jit/bitsetasshortlong.h b/src/jit/bitsetasshortlong.h
index 128c0aab3f..17e0e3a69c 100644
--- a/src/jit/bitsetasshortlong.h
+++ b/src/jit/bitsetasshortlong.h
@@ -313,7 +313,10 @@ public:
{
if (IsShort(env))
{
- (size_t&)bs1 &= (size_t)bs2;
+ size_t val = (size_t)bs1;
+
+ val &= (size_t)bs2;
+ bs1 = (BitSetShortLongRep)val;
}
else
{
diff --git a/src/jit/block.cpp b/src/jit/block.cpp
index 9598366828..8d1a83f5b4 100644
--- a/src/jit/block.cpp
+++ b/src/jit/block.cpp
@@ -1357,3 +1357,143 @@ BasicBlock* Compiler::bbNewBasicBlock(BBjumpKinds jumpKind)
return block;
}
+
+//------------------------------------------------------------------------------
+// DisplayStaticSizes: display various static sizes of the BasicBlock data structure.
+//
+// Arguments:
+// fout - where to write the output
+//
+// Return Value:
+// None
+//
+// Note: This function only does something if MEASURE_BLOCK_SIZE is defined, which it might
+// be in private Release builds.
+//
+/* static */
+void BasicBlock::DisplayStaticSizes(FILE* fout)
+{
+#if MEASURE_BLOCK_SIZE
+
+ BasicBlock* bbDummy = nullptr;
+
+ fprintf(fout, "\n");
+ fprintf(fout, "Offset / size of bbNext = %3u / %3u\n", offsetof(BasicBlock, bbNext),
+ sizeof(bbDummy->bbNext));
+ fprintf(fout, "Offset / size of bbPrev = %3u / %3u\n", offsetof(BasicBlock, bbPrev),
+ sizeof(bbDummy->bbPrev));
+ fprintf(fout, "Offset / size of bbFlags = %3u / %3u\n", offsetof(BasicBlock, bbFlags),
+ sizeof(bbDummy->bbFlags));
+ fprintf(fout, "Offset / size of bbNum = %3u / %3u\n", offsetof(BasicBlock, bbNum),
+ sizeof(bbDummy->bbNum));
+ fprintf(fout, "Offset / size of bbPostOrderNum = %3u / %3u\n", offsetof(BasicBlock, bbPostOrderNum),
+ sizeof(bbDummy->bbPostOrderNum));
+ fprintf(fout, "Offset / size of bbRefs = %3u / %3u\n", offsetof(BasicBlock, bbRefs),
+ sizeof(bbDummy->bbRefs));
+ fprintf(fout, "Offset / size of bbWeight = %3u / %3u\n", offsetof(BasicBlock, bbWeight),
+ sizeof(bbDummy->bbWeight));
+ fprintf(fout, "Offset / size of bbJumpKind = %3u / %3u\n", offsetof(BasicBlock, bbJumpKind),
+ sizeof(bbDummy->bbJumpKind));
+ fprintf(fout, "Offset / size of bbJumpOffs = %3u / %3u\n", offsetof(BasicBlock, bbJumpOffs),
+ sizeof(bbDummy->bbJumpOffs));
+ fprintf(fout, "Offset / size of bbJumpDest = %3u / %3u\n", offsetof(BasicBlock, bbJumpDest),
+ sizeof(bbDummy->bbJumpDest));
+ fprintf(fout, "Offset / size of bbJumpSwt = %3u / %3u\n", offsetof(BasicBlock, bbJumpSwt),
+ sizeof(bbDummy->bbJumpSwt));
+ fprintf(fout, "Offset / size of bbEntryState = %3u / %3u\n", offsetof(BasicBlock, bbEntryState),
+ sizeof(bbDummy->bbEntryState));
+ fprintf(fout, "Offset / size of bbStkTempsIn = %3u / %3u\n", offsetof(BasicBlock, bbStkTempsIn),
+ sizeof(bbDummy->bbStkTempsIn));
+ fprintf(fout, "Offset / size of bbStkTempsOut = %3u / %3u\n", offsetof(BasicBlock, bbStkTempsOut),
+ sizeof(bbDummy->bbStkTempsOut));
+ fprintf(fout, "Offset / size of bbTryIndex = %3u / %3u\n", offsetof(BasicBlock, bbTryIndex),
+ sizeof(bbDummy->bbTryIndex));
+ fprintf(fout, "Offset / size of bbHndIndex = %3u / %3u\n", offsetof(BasicBlock, bbHndIndex),
+ sizeof(bbDummy->bbHndIndex));
+ fprintf(fout, "Offset / size of bbCatchTyp = %3u / %3u\n", offsetof(BasicBlock, bbCatchTyp),
+ sizeof(bbDummy->bbCatchTyp));
+ fprintf(fout, "Offset / size of bbStkDepth = %3u / %3u\n", offsetof(BasicBlock, bbStkDepth),
+ sizeof(bbDummy->bbStkDepth));
+ fprintf(fout, "Offset / size of bbFPinVars = %3u / %3u\n", offsetof(BasicBlock, bbFPinVars),
+ sizeof(bbDummy->bbFPinVars));
+ fprintf(fout, "Offset / size of bbCheapPreds = %3u / %3u\n", offsetof(BasicBlock, bbCheapPreds),
+ sizeof(bbDummy->bbCheapPreds));
+ fprintf(fout, "Offset / size of bbPreds = %3u / %3u\n", offsetof(BasicBlock, bbPreds),
+ sizeof(bbDummy->bbPreds));
+ fprintf(fout, "Offset / size of bbReach = %3u / %3u\n", offsetof(BasicBlock, bbReach),
+ sizeof(bbDummy->bbReach));
+ fprintf(fout, "Offset / size of bbIDom = %3u / %3u\n", offsetof(BasicBlock, bbIDom),
+ sizeof(bbDummy->bbIDom));
+ fprintf(fout, "Offset / size of bbDfsNum = %3u / %3u\n", offsetof(BasicBlock, bbDfsNum),
+ sizeof(bbDummy->bbDfsNum));
+ fprintf(fout, "Offset / size of bbCodeOffs = %3u / %3u\n", offsetof(BasicBlock, bbCodeOffs),
+ sizeof(bbDummy->bbCodeOffs));
+ fprintf(fout, "Offset / size of bbCodeOffsEnd = %3u / %3u\n", offsetof(BasicBlock, bbCodeOffsEnd),
+ sizeof(bbDummy->bbCodeOffsEnd));
+ fprintf(fout, "Offset / size of bbVarUse = %3u / %3u\n", offsetof(BasicBlock, bbVarUse),
+ sizeof(bbDummy->bbVarUse));
+ fprintf(fout, "Offset / size of bbVarDef = %3u / %3u\n", offsetof(BasicBlock, bbVarDef),
+ sizeof(bbDummy->bbVarDef));
+ fprintf(fout, "Offset / size of bbLiveIn = %3u / %3u\n", offsetof(BasicBlock, bbLiveIn),
+ sizeof(bbDummy->bbLiveIn));
+ fprintf(fout, "Offset / size of bbLiveOut = %3u / %3u\n", offsetof(BasicBlock, bbLiveOut),
+ sizeof(bbDummy->bbLiveOut));
+ // Can't do bitfield bbMemoryUse, bbMemoryDef, bbMemoryLiveIn, bbMemoryLiveOut, bbMemoryHavoc
+ fprintf(fout, "Offset / size of bbMemorySsaPhiFunc = %3u / %3u\n", offsetof(BasicBlock, bbMemorySsaPhiFunc),
+ sizeof(bbDummy->bbMemorySsaPhiFunc));
+ fprintf(fout, "Offset / size of bbMemorySsaNumIn = %3u / %3u\n", offsetof(BasicBlock, bbMemorySsaNumIn),
+ sizeof(bbDummy->bbMemorySsaNumIn));
+ fprintf(fout, "Offset / size of bbMemorySsaNumOut = %3u / %3u\n", offsetof(BasicBlock, bbMemorySsaNumOut),
+ sizeof(bbDummy->bbMemorySsaNumOut));
+ fprintf(fout, "Offset / size of bbScope = %3u / %3u\n", offsetof(BasicBlock, bbScope),
+ sizeof(bbDummy->bbScope));
+ fprintf(fout, "Offset / size of bbCseGen = %3u / %3u\n", offsetof(BasicBlock, bbCseGen),
+ sizeof(bbDummy->bbCseGen));
+#if ASSERTION_PROP
+ fprintf(fout, "Offset / size of bbAssertionGen = %3u / %3u\n", offsetof(BasicBlock, bbAssertionGen),
+ sizeof(bbDummy->bbAssertionGen));
+#endif // ASSERTION_PROP
+ fprintf(fout, "Offset / size of bbCseIn = %3u / %3u\n", offsetof(BasicBlock, bbCseIn),
+ sizeof(bbDummy->bbCseIn));
+#if ASSERTION_PROP
+ fprintf(fout, "Offset / size of bbAssertionIn = %3u / %3u\n", offsetof(BasicBlock, bbAssertionIn),
+ sizeof(bbDummy->bbAssertionIn));
+#endif // ASSERTION_PROP
+ fprintf(fout, "Offset / size of bbCseOut = %3u / %3u\n", offsetof(BasicBlock, bbCseOut),
+ sizeof(bbDummy->bbCseOut));
+#if ASSERTION_PROP
+ fprintf(fout, "Offset / size of bbAssertionOut = %3u / %3u\n", offsetof(BasicBlock, bbAssertionOut),
+ sizeof(bbDummy->bbAssertionOut));
+#endif // ASSERTION_PROP
+ fprintf(fout, "Offset / size of bbEmitCookie = %3u / %3u\n", offsetof(BasicBlock, bbEmitCookie),
+ sizeof(bbDummy->bbEmitCookie));
+
+#if FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
+ fprintf(fout, "Offset / size of bbUnwindNopEmitCookie = %3u / %3u\n", offsetof(BasicBlock, bbUnwindNopEmitCookie),
+ sizeof(bbDummy->bbUnwindNopEmitCookie));
+#endif // FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
+
+#ifdef VERIFIER
+ fprintf(fout, "Offset / size of bbStackIn = %3u / %3u\n", offsetof(BasicBlock, bbStackIn),
+ sizeof(bbDummy->bbStackIn));
+ fprintf(fout, "Offset / size of bbStackOut = %3u / %3u\n", offsetof(BasicBlock, bbStackOut),
+ sizeof(bbDummy->bbStackOut));
+ fprintf(fout, "Offset / size of bbTypesIn = %3u / %3u\n", offsetof(BasicBlock, bbTypesIn),
+ sizeof(bbDummy->bbTypesIn));
+ fprintf(fout, "Offset / size of bbTypesOut = %3u / %3u\n", offsetof(BasicBlock, bbTypesOut),
+ sizeof(bbDummy->bbTypesOut));
+#endif // VERIFIER
+
+#ifdef DEBUG
+ fprintf(fout, "Offset / size of bbLoopNum = %3u / %3u\n", offsetof(BasicBlock, bbLoopNum),
+ sizeof(bbDummy->bbLoopNum));
+#endif // DEBUG
+
+ fprintf(fout, "Offset / size of bbNatLoopNum = %3u / %3u\n", offsetof(BasicBlock, bbNatLoopNum),
+ sizeof(bbDummy->bbNatLoopNum));
+
+ fprintf(fout, "\n");
+ fprintf(fout, "Size of BasicBlock = %3u\n", sizeof(BasicBlock));
+
+#endif // MEASURE_BLOCK_SIZE
+}
diff --git a/src/jit/block.h b/src/jit/block.h
index d447429542..56b1c7313d 100644
--- a/src/jit/block.h
+++ b/src/jit/block.h
@@ -1183,6 +1183,8 @@ struct BasicBlock : private LIR::Range
return false;
}
#endif // DEBUG
+
+ static void DisplayStaticSizes(FILE* fout);
};
template <>
diff --git a/src/jit/codegen.h b/src/jit/codegen.h
index c4af197d2b..046addff4c 100644
--- a/src/jit/codegen.h
+++ b/src/jit/codegen.h
@@ -126,10 +126,6 @@ private:
bool genUseBlockInit; // true if we plan to block-initialize the local stack frame
unsigned genInitStkLclCnt; // The count of local variables that we need to zero init
- // Keeps track of how many bytes we've pushed on the processor's stack.
- //
- unsigned genStackLevel;
-
void SubtractStackLevel(unsigned adjustment)
{
assert(genStackLevel >= adjustment);
@@ -513,7 +509,7 @@ protected:
#ifdef _TARGET_ARM64_
virtual void SetSaveFpLrWithAllCalleeSavedRegisters(bool value);
- virtual bool IsSaveFpLrWithAllCalleeSavedRegisters();
+ virtual bool IsSaveFpLrWithAllCalleeSavedRegisters() const;
bool genSaveFpLrWithAllCalleeSavedRegisters;
#endif // _TARGET_ARM64_
@@ -561,8 +557,9 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
siVarLoc* varLoc);
void genSetScopeInfo();
+#ifdef USING_SCOPE_INFO
+ void genSetScopeInfoUsingsiScope();
-protected:
/*
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
@@ -668,7 +665,6 @@ public:
const char* siStackVarName(size_t offs, size_t size, unsigned reg, unsigned stkOffs);
#endif // LATE_DISASM
-public:
/*
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
@@ -749,14 +745,16 @@ protected:
void psiEndPrologScope(psiScope* scope);
void psSetScopeOffset(psiScope* newScope, LclVarDsc* lclVarDsc1);
+#endif // USING_SCOPE_INFO
-/*****************************************************************************
- * TrnslLocalVarInfo
- *
- * This struct holds the LocalVarInfo in terms of the generated native code
- * after a call to genSetScopeInfo()
- */
+ /*****************************************************************************
+ * TrnslLocalVarInfo
+ *
+ * This struct holds the LocalVarInfo in terms of the generated native code
+ * after a call to genSetScopeInfo()
+ */
+protected:
#ifdef DEBUG
struct TrnslLocalVarInfo
diff --git a/src/jit/codegenarm64.cpp b/src/jit/codegenarm64.cpp
index 9d1959e850..9cfaccef08 100644
--- a/src/jit/codegenarm64.cpp
+++ b/src/jit/codegenarm64.cpp
@@ -2198,7 +2198,7 @@ void CodeGen::genLclHeap(GenTree* tree)
{
regCnt = tree->ExtractTempReg();
}
- genSetRegToIcon(regCnt, amount, ((int)amount == amount) ? TYP_INT : TYP_LONG);
+ genSetRegToIcon(regCnt, amount, ((unsigned int)amount == amount) ? TYP_INT : TYP_LONG);
}
if (compiler->info.compInitMem)
@@ -2484,7 +2484,7 @@ void CodeGen::genCodeForInitBlkUnroll(GenTreeBlk* initBlkNode)
}
assert(dstAddr->isUsedFromReg());
- assert(initVal->isUsedFromReg() && !initVal->IsIntegralConst(0) || initVal->IsIntegralConst(0));
+ assert((initVal->isUsedFromReg() && !initVal->IsIntegralConst(0)) || initVal->IsIntegralConst(0));
assert(size != 0);
assert(size <= INITBLK_UNROLL_LIMIT);
@@ -3695,7 +3695,7 @@ void CodeGen::genCodeForJumpCompare(GenTreeOp* tree)
// genSPtoFPdelta - return offset from the stack pointer (Initial-SP) to the frame pointer. The frame pointer
// will point to the saved frame pointer slot (i.e., there will be frame pointer chaining).
//
-int CodeGenInterface::genSPtoFPdelta()
+int CodeGenInterface::genSPtoFPdelta() const
{
assert(isFramePointerUsed());
int delta = -1; // initialization to illegal value
@@ -3725,7 +3725,7 @@ int CodeGenInterface::genSPtoFPdelta()
// Total frame size
//
-int CodeGenInterface::genTotalFrameSize()
+int CodeGenInterface::genTotalFrameSize() const
{
// For varargs functions, we home all the incoming register arguments. They are not
// included in the compCalleeRegsPushed count. This is like prespill on ARM32, but
@@ -3748,7 +3748,7 @@ int CodeGenInterface::genTotalFrameSize()
//
// There must be a frame pointer to call this function!
-int CodeGenInterface::genCallerSPtoFPdelta()
+int CodeGenInterface::genCallerSPtoFPdelta() const
{
assert(isFramePointerUsed());
int callerSPtoFPdelta;
@@ -3764,7 +3764,7 @@ int CodeGenInterface::genCallerSPtoFPdelta()
//
// This number will be negative.
-int CodeGenInterface::genCallerSPtoInitialSPdelta()
+int CodeGenInterface::genCallerSPtoInitialSPdelta() const
{
int callerSPtoSPdelta = 0;
@@ -3788,7 +3788,7 @@ void CodeGen::SetSaveFpLrWithAllCalleeSavedRegisters(bool value)
// IsSaveFpLrWithAllCalleeSavedRegisters - Return the value that indicates where FP/LR registers
// are stored in the prolog.
//
-bool CodeGen::IsSaveFpLrWithAllCalleeSavedRegisters()
+bool CodeGen::IsSaveFpLrWithAllCalleeSavedRegisters() const
{
return genSaveFpLrWithAllCalleeSavedRegisters;
}
diff --git a/src/jit/codegenarmarch.cpp b/src/jit/codegenarmarch.cpp
index 50180133d4..0c84bedb75 100644
--- a/src/jit/codegenarmarch.cpp
+++ b/src/jit/codegenarmarch.cpp
@@ -3860,11 +3860,12 @@ void CodeGen::genAllocLclFrame(unsigned frameSize, regNumber initReg, bool* pIni
#ifdef _TARGET_ARM_
compiler->unwindAllocStack(frameSize);
-
+#ifdef USING_SCOPE_INFO
if (!doubleAlignOrFramePointerUsed())
{
psiAdjustStackLevel(frameSize);
}
+#endif // USING_SCOPE_INFO
#endif // _TARGET_ARM_
}
diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp
index a45710330f..9efa517edb 100644
--- a/src/jit/codegencommon.cpp
+++ b/src/jit/codegencommon.cpp
@@ -191,7 +191,7 @@ void CodeGenInterface::genMarkTreeInReg(GenTree* tree, regNumber reg)
// Return value:
// Frame size
-int CodeGenInterface::genTotalFrameSize()
+int CodeGenInterface::genTotalFrameSize() const
{
assert(!IsUninitialized(compiler->compCalleeRegsPushed));
@@ -208,7 +208,7 @@ int CodeGenInterface::genTotalFrameSize()
//
// There must be a frame pointer to call this function!
-int CodeGenInterface::genSPtoFPdelta()
+int CodeGenInterface::genSPtoFPdelta() const
{
assert(isFramePointerUsed());
@@ -227,7 +227,7 @@ int CodeGenInterface::genSPtoFPdelta()
//
// There must be a frame pointer to call this function!
-int CodeGenInterface::genCallerSPtoFPdelta()
+int CodeGenInterface::genCallerSPtoFPdelta() const
{
assert(isFramePointerUsed());
int callerSPtoFPdelta = 0;
@@ -255,7 +255,7 @@ int CodeGenInterface::genCallerSPtoFPdelta()
//
// This number will be negative.
-int CodeGenInterface::genCallerSPtoInitialSPdelta()
+int CodeGenInterface::genCallerSPtoInitialSPdelta() const
{
int callerSPtoSPdelta = 0;
@@ -732,8 +732,9 @@ void Compiler::compChangeLife(VARSET_VALARG_TP newLife)
JITDUMP("\t\t\t\t\t\t\tV%02u becoming live\n", varNum);
}
}
-
+#ifdef USING_SCOPE_INFO
codeGen->siUpdate();
+#endif // USING_SCOPE_INFO
}
// Need an explicit instantiation.
@@ -2338,7 +2339,6 @@ void CodeGen::genGenerateCode(void** codePtr, ULONG* nativeSizeOfCode)
if (compiler->opts.dmpHex)
{
size_t consSize = getEmitter()->emitDataSize();
- size_t infoSize = compiler->compInfoBlkSize;
fprintf(dmpf, "Generated code for %s:\n", compiler->info.compFullName);
fprintf(dmpf, "\n");
@@ -2352,6 +2352,7 @@ void CodeGen::genGenerateCode(void** codePtr, ULONG* nativeSizeOfCode)
fprintf(dmpf, " Const at %p [%04X bytes]\n", dspPtr(consPtr), consSize);
}
#ifdef JIT32_GCENCODER
+ size_t infoSize = compiler->compInfoBlkSize;
if (infoSize)
fprintf(dmpf, " Info at %p [%04X bytes]\n", dspPtr(infoPtr), infoSize);
#endif // JIT32_GCENCODER
@@ -3804,11 +3805,12 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere
assert(varDsc->lvSize() >= baseOffset + (unsigned)size);
}
#endif // !UNIX_AMD64_ABI
-
+#ifdef USING_SCOPE_INFO
if (regArgTab[argNum].slot == 1)
{
psiMoveToStack(varNum);
}
+#endif // USING_SCOPE_INFO
}
/* mark the argument as processed */
@@ -3952,9 +3954,10 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere
regArgMaskLive &= ~genRegMask(varDscSrc->lvArgReg);
regArgMaskLive &= ~genRegMask(varDscDest->lvArgReg);
-
+#ifdef USING_SCOPE_INFO
psiMoveToReg(varNumSrc);
psiMoveToReg(varNumDest);
+#endif // USING_SCOPE_INFO
}
else
#endif // _TARGET_XARCH_
@@ -4016,9 +4019,9 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere
regSet.verifyRegUsed(xtraReg);
*pXtraRegClobbered = true;
-
+#ifdef USING_SCOPE_INFO
psiMoveToReg(varNumDest, xtraReg);
-
+#endif // USING_SCOPE_INFO
/* start moving everything to its right place */
while (srcReg != begReg)
@@ -4083,9 +4086,9 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere
getEmitter()->emitIns_R_R(insCopy, size, destRegNum, xtraReg);
regSet.verifyRegUsed(destRegNum);
-
+#ifdef USING_SCOPE_INFO
psiMoveToReg(varNumSrc);
-
+#endif // USING_SCOPE_INFO
/* mark the beginning register as processed */
regArgTab[srcReg].processed = true;
@@ -4269,8 +4272,9 @@ void CodeGen::genFnPrologCalleeRegArgs(regNumber xtraReg, bool* pXtraRegClobbere
#endif
getEmitter()->emitIns_R_R(ins_Copy(destMemType), size, destRegNum, regNum);
-
+#ifdef USING_SCOPE_INFO
psiMoveToReg(varNum);
+#endif // USING_SCOPE_INFO
}
/* mark the argument as processed */
@@ -4404,8 +4408,9 @@ void CodeGen::genEnregisterIncomingStackArgs()
getEmitter()->emitIns_R_S(ins_Load(type), emitTypeSize(type), regNum, varNum, 0);
regSet.verifyRegUsed(regNum);
-
+#ifdef USING_SCOPE_INFO
psiMoveToReg(varNum);
+#endif // USING_SCOPE_INFO
}
}
@@ -5247,12 +5252,12 @@ void CodeGen::genPushCalleeSavedRegisters()
{
inst_RV(INS_push, reg, TYP_REF);
compiler->unwindPush(reg);
-
+#ifdef USING_SCOPE_INFO
if (!doubleAlignOrFramePointerUsed())
{
psiAdjustStackLevel(REGSIZE_BYTES);
}
-
+#endif // USING_SCOPE_INFO
rsPushRegs &= ~regBit;
}
}
@@ -6075,9 +6080,8 @@ void CodeGen::genZeroInitFrame(int untrLclHi, int untrLclLo, regNumber initReg,
#else // !define(_TARGET_ARM_)
- regNumber rZero1 = REG_ZR;
- rAddr = initReg;
- *pInitRegZeroed = false;
+ rAddr = initReg;
+ *pInitRegZeroed = false;
#endif // !defined(_TARGET_ARM_)
@@ -7531,7 +7535,9 @@ void CodeGen::genEstablishFramePointer(int delta, bool reportUnwindData)
if (delta == 0)
{
getEmitter()->emitIns_R_R(INS_mov, EA_PTRSIZE, REG_FPBASE, REG_SPBASE);
+#ifdef USING_SCOPE_INFO
psiMoveESPtoEBP();
+#endif // USING_SCOPE_INFO
}
else
{
@@ -7633,12 +7639,13 @@ void CodeGen::genFnProlog()
printf("\n__prolog:\n");
}
#endif
-
+#ifdef USING_SCOPE_INFO
if (compiler->opts.compScopeInfo && (compiler->info.compVarScopesCount > 0))
{
// Create new scopes for the method-parameters for the prolog-block.
psiBegProlog();
}
+#endif // USING_SCOPE_INFO
#ifdef DEBUG
@@ -7963,8 +7970,9 @@ void CodeGen::genFnProlog()
{
inst_RV(INS_push, REG_FPBASE, TYP_REF);
compiler->unwindPush(REG_FPBASE);
+#ifdef USING_SCOPE_INFO
psiAdjustStackLevel(REGSIZE_BYTES);
-
+#endif // USING_SCOPE_INFO
#ifndef _TARGET_AMD64_ // On AMD64, establish the frame pointer after the "sub rsp"
genEstablishFramePointer(0, /*reportUnwindData*/ true);
#endif // !_TARGET_AMD64_
@@ -8284,12 +8292,12 @@ void CodeGen::genFnProlog()
genPrologPadForReJit();
getEmitter()->emitMarkPrologEnd();
}
-
+#ifdef USING_SCOPE_INFO
if (compiler->opts.compScopeInfo && (compiler->info.compVarScopesCount > 0))
{
psiEndProlog();
}
-
+#endif // USING_SCOPE_INFO
if (hasGCRef)
{
getEmitter()->emitSetFrameRangeGCRs(GCrefLo, GCrefHi);
@@ -10486,27 +10494,40 @@ void CodeGen::genSetScopeInfo()
}
noway_assert(compiler->opts.compScopeInfo && (compiler->info.compVarScopesCount > 0));
- noway_assert(psiOpenScopeList.scNext == nullptr);
-
- unsigned i;
- unsigned scopeCnt = siScopeCnt + psiScopeCnt;
- compiler->eeSetLVcount(scopeCnt);
+ unsigned varsHomeCount = 0;
+#ifdef USING_SCOPE_INFO
+ varsHomeCount = siScopeCnt + psiScopeCnt;
+#endif // USING_SCOPE_INFO
+ compiler->eeSetLVcount(varsHomeCount);
#ifdef DEBUG
- genTrnslLocalVarCount = scopeCnt;
- if (scopeCnt)
+ genTrnslLocalVarCount = varsHomeCount;
+ if (varsHomeCount)
{
- genTrnslLocalVarInfo = new (compiler, CMK_DebugOnly) TrnslLocalVarInfo[scopeCnt];
+ genTrnslLocalVarInfo = new (compiler, CMK_DebugOnly) TrnslLocalVarInfo[varsHomeCount];
}
#endif
+#ifdef USING_SCOPE_INFO
+ genSetScopeInfoUsingsiScope();
+#endif // USING_SCOPE_INFO
+
+ compiler->eeSetLVdone();
+}
+
+#ifdef USING_SCOPE_INFO
+void CodeGen::genSetScopeInfoUsingsiScope()
+{
+ noway_assert(psiOpenScopeList.scNext == nullptr);
+
// Record the scopes found for the parameters over the prolog.
// The prolog needs to be treated differently as a variable may not
// have the same info in the prolog block as is given by compiler->lvaTable.
// eg. A register parameter is actually on the stack, before it is loaded to reg.
CodeGen::psiScope* scopeP;
+ unsigned i;
for (i = 0, scopeP = psiScopeList.scNext; i < psiScopeCnt; i++, scopeP = scopeP->scNext)
{
@@ -10563,9 +10584,8 @@ void CodeGen::genSetScopeInfo()
genSetScopeInfo(psiScopeCnt + i, startOffs, endOffs - startOffs, scopeL->scVarNum, scopeL->scLVnum,
scopeL->scAvailable, &varLoc);
}
-
- compiler->eeSetLVdone();
}
+#endif // USING_SCOPE_INFO
//------------------------------------------------------------------------
// genSetScopeInfo: Record scope information for debug info
@@ -10816,7 +10836,7 @@ void CodeGen::genIPmappingAdd(IL_OFFSETX offsx, bool isLabel)
default:
- if (offsx != ICorDebugInfo::NO_MAPPING)
+ if (offsx != (IL_OFFSETX)ICorDebugInfo::NO_MAPPING)
{
noway_assert(jitGetILoffs(offsx) <= compiler->info.compILCodeSize);
}
@@ -11557,3 +11577,8 @@ void CodeGen::genStackPointerCheck(bool doStackPointerCheck, unsigned lvaStackPo
}
#endif // defined(DEBUG) && defined(_TARGET_XARCH_)
+
+unsigned CodeGenInterface::getCurrentStackLevel() const
+{
+ return genStackLevel;
+}
diff --git a/src/jit/codegeninterface.h b/src/jit/codegeninterface.h
index 01febd4d52..6935a77e3f 100644
--- a/src/jit/codegeninterface.h
+++ b/src/jit/codegeninterface.h
@@ -25,6 +25,12 @@
#include "jitgcinfo.h"
#include "treelifeupdater.h"
+// Disable this flag to avoid using psiScope/siScope info to report reporting
+// variables' home location during the method/prolog code.
+#if 1
+#define USING_SCOPE_INFO
+#endif // USING_SCOPE_INFO
+
// Forward reference types
class CodeGenInterface;
@@ -171,14 +177,14 @@ public:
}
public:
- int genCallerSPtoFPdelta();
- int genCallerSPtoInitialSPdelta();
- int genSPtoFPdelta();
- int genTotalFrameSize();
+ int genCallerSPtoFPdelta() const;
+ int genCallerSPtoInitialSPdelta() const;
+ int genSPtoFPdelta() const;
+ int genTotalFrameSize() const;
#ifdef _TARGET_ARM64_
virtual void SetSaveFpLrWithAllCalleeSavedRegisters(bool value) = 0;
- virtual bool IsSaveFpLrWithAllCalleeSavedRegisters() = 0;
+ virtual bool IsSaveFpLrWithAllCalleeSavedRegisters() const = 0;
#endif // _TARGET_ARM64_
regNumber genGetThisArgReg(GenTreeCall* call) const;
@@ -304,7 +310,7 @@ public:
TempDsc* getSpillTempDsc(GenTree* tree);
public:
- emitter* getEmitter()
+ emitter* getEmitter() const
{
return m_cgEmitter;
}
@@ -385,7 +391,9 @@ private:
bool m_cgFullPtrRegMap;
public:
+#ifdef USING_SCOPE_INFO
virtual void siUpdate() = 0;
+#endif // USING_SCOPE_INFO
/* These are the different addressing modes used to access a local var.
* The JIT has to report the location of the locals back to the EE
@@ -512,8 +520,8 @@ public:
// Helper functions
- bool vlIsInReg(regNumber reg);
- bool vlIsOnStk(regNumber reg, signed offset);
+ bool vlIsInReg(regNumber reg) const;
+ bool vlIsOnStk(regNumber reg, signed offset) const;
siVarLoc(const LclVarDsc* varDsc, regNumber baseReg, int offset, bool isFramePointerUsed);
siVarLoc(){};
@@ -534,6 +542,13 @@ public:
const LclVarDsc* varDsc, var_types type, regNumber baseReg, int offset, bool isFramePointerUsed);
};
+public:
+ unsigned getCurrentStackLevel() const;
+
+protected:
+ // Keeps track of how many bytes we've pushed on the processor's stack.
+ unsigned genStackLevel;
+
#ifdef LATE_DISASM
public:
virtual const char* siRegVarName(size_t offs, size_t size, unsigned reg) = 0;
diff --git a/src/jit/codegenlinear.cpp b/src/jit/codegenlinear.cpp
index 149bb3333f..7c5c01895e 100644
--- a/src/jit/codegenlinear.cpp
+++ b/src/jit/codegenlinear.cpp
@@ -84,12 +84,13 @@ void CodeGen::genInitializeRegisterState()
// iterated.
void CodeGen::genInitialize()
{
+#ifdef USING_SCOPE_INFO
// Initialize the line# tracking logic
-
if (compiler->opts.compScopeInfo)
{
siInit();
}
+#endif // USING_SCOPE_INFO
// The current implementation of switch tables requires the first block to have a label so it
// can generate offsets to the switch label targets.
@@ -113,6 +114,10 @@ void CodeGen::genInitialize()
// Make sure a set is allocated for compiler->compCurLife (in the long case), so we can set it to empty without
// allocation at the start of each basic block.
VarSetOps::AssignNoCopy(compiler, compiler->compCurLife, VarSetOps::MakeEmpty(compiler));
+
+ // We initialize the stack level before first "BasicBlock" code is generated in case we need to report stack
+ // variable needs home and so its stack offset.
+ SetStackLevel(0);
}
//------------------------------------------------------------------------
@@ -350,9 +355,9 @@ void CodeGen::genCodeForBBlist()
/* Tell everyone which basic block we're working on */
compiler->compCurBB = block;
-
+#ifdef USING_SCOPE_INFO
siBeginBlock(block);
-
+#endif // USING_SCOPE_INFO
// BBF_INTERNAL blocks don't correspond to any single IL instruction.
if (compiler->opts.compDbgInfo && (block->bbFlags & BBF_INTERNAL) &&
!compiler->fgBBisScratch(block)) // If the block is the distinguished first scratch block, then no need to
@@ -508,7 +513,7 @@ void CodeGen::genCodeForBBlist()
// This can lead to problems when debugging the generated code. To prevent these issues, make sure
// we've generated code for the last IL offset we saw in the block.
genEnsureCodeEmitted(currentILOffset);
-
+#ifdef USING_SCOPE_INFO
if (compiler->opts.compScopeInfo && (compiler->info.compVarScopesCount > 0))
{
siEndBlock(block);
@@ -533,6 +538,7 @@ void CodeGen::genCodeForBBlist()
siCloseAllOpenScopes();
}
}
+#endif // USING_SCOPE_INFO
SubtractStackLevel(savedStkLvl);
@@ -1141,7 +1147,7 @@ void CodeGen::genConsumeRegAndCopy(GenTree* node, regNumber needReg)
{
return;
}
- regNumber treeReg = genConsumeReg(node);
+ genConsumeReg(node);
genCopyRegIfNeeded(node, needReg);
}
@@ -1840,9 +1846,8 @@ void CodeGen::genProduceReg(GenTree* tree)
for (unsigned i = 0; i < regCount; ++i)
{
- var_types type = retTypeDesc->GetReturnRegType(i);
- regNumber fromReg = call->GetRegNumByIdx(i);
- regNumber toReg = copy->GetRegNumByIdx(i);
+ var_types type = retTypeDesc->GetReturnRegType(i);
+ regNumber toReg = copy->GetRegNumByIdx(i);
if (toReg != REG_NA)
{
diff --git a/src/jit/codegenxarch.cpp b/src/jit/codegenxarch.cpp
index d7b15025a3..9256650978 100644
--- a/src/jit/codegenxarch.cpp
+++ b/src/jit/codegenxarch.cpp
@@ -2333,11 +2333,12 @@ void CodeGen::genAllocLclFrame(unsigned frameSize, regNumber initReg, bool* pIni
}
compiler->unwindAllocStack(frameSize);
-
+#ifdef USING_SCOPE_INFO
if (!doubleAlignOrFramePointerUsed())
{
psiAdjustStackLevel(frameSize);
}
+#endif // USING_SCOPE_INFO
}
//------------------------------------------------------------------------
@@ -6852,7 +6853,7 @@ void CodeGen::genCkfinite(GenTree* treeNode)
}
#ifdef _TARGET_AMD64_
-int CodeGenInterface::genSPtoFPdelta()
+int CodeGenInterface::genSPtoFPdelta() const
{
int delta;
@@ -6904,7 +6905,7 @@ int CodeGenInterface::genSPtoFPdelta()
// Total frame size
//
-int CodeGenInterface::genTotalFrameSize()
+int CodeGenInterface::genTotalFrameSize() const
{
assert(!IsUninitialized(compiler->compCalleeRegsPushed));
@@ -6925,7 +6926,7 @@ int CodeGenInterface::genTotalFrameSize()
// is based on a maximum delta from Initial-SP, so first we find SP, then
// compute the FP offset.
-int CodeGenInterface::genCallerSPtoFPdelta()
+int CodeGenInterface::genCallerSPtoFPdelta() const
{
assert(isFramePointerUsed());
int callerSPtoFPdelta;
@@ -6941,7 +6942,7 @@ int CodeGenInterface::genCallerSPtoFPdelta()
//
// This number will be negative.
-int CodeGenInterface::genCallerSPtoInitialSPdelta()
+int CodeGenInterface::genCallerSPtoInitialSPdelta() const
{
int callerSPtoSPdelta = 0;
diff --git a/src/jit/compiler.cpp b/src/jit/compiler.cpp
index 3887f0ea98..a594e23251 100644
--- a/src/jit/compiler.cpp
+++ b/src/jit/compiler.cpp
@@ -1783,115 +1783,11 @@ void Compiler::compShutdown()
/* static */
void Compiler::compDisplayStaticSizes(FILE* fout)
{
-
#if MEASURE_NODE_SIZE
GenTree::DumpNodeSizes(fout);
#endif
-#if MEASURE_BLOCK_SIZE
-
- BasicBlock* bbDummy = nullptr;
-
- fprintf(fout, "\n");
- fprintf(fout, "Offset / size of bbNext = %3u / %3u\n", offsetof(BasicBlock, bbNext),
- sizeof(bbDummy->bbNext));
- fprintf(fout, "Offset / size of bbNum = %3u / %3u\n", offsetof(BasicBlock, bbNum),
- sizeof(bbDummy->bbNum));
- fprintf(fout, "Offset / size of bbPostOrderNum = %3u / %3u\n", offsetof(BasicBlock, bbPostOrderNum),
- sizeof(bbDummy->bbPostOrderNum));
- fprintf(fout, "Offset / size of bbRefs = %3u / %3u\n", offsetof(BasicBlock, bbRefs),
- sizeof(bbDummy->bbRefs));
- fprintf(fout, "Offset / size of bbFlags = %3u / %3u\n", offsetof(BasicBlock, bbFlags),
- sizeof(bbDummy->bbFlags));
- fprintf(fout, "Offset / size of bbWeight = %3u / %3u\n", offsetof(BasicBlock, bbWeight),
- sizeof(bbDummy->bbWeight));
- fprintf(fout, "Offset / size of bbJumpKind = %3u / %3u\n", offsetof(BasicBlock, bbJumpKind),
- sizeof(bbDummy->bbJumpKind));
- fprintf(fout, "Offset / size of bbJumpOffs = %3u / %3u\n", offsetof(BasicBlock, bbJumpOffs),
- sizeof(bbDummy->bbJumpOffs));
- fprintf(fout, "Offset / size of bbJumpDest = %3u / %3u\n", offsetof(BasicBlock, bbJumpDest),
- sizeof(bbDummy->bbJumpDest));
- fprintf(fout, "Offset / size of bbJumpSwt = %3u / %3u\n", offsetof(BasicBlock, bbJumpSwt),
- sizeof(bbDummy->bbJumpSwt));
- fprintf(fout, "Offset / size of bbEntryState = %3u / %3u\n", offsetof(BasicBlock, bbEntryState),
- sizeof(bbDummy->bbEntryState));
- fprintf(fout, "Offset / size of bbStkTempsIn = %3u / %3u\n", offsetof(BasicBlock, bbStkTempsIn),
- sizeof(bbDummy->bbStkTempsIn));
- fprintf(fout, "Offset / size of bbStkTempsOut = %3u / %3u\n", offsetof(BasicBlock, bbStkTempsOut),
- sizeof(bbDummy->bbStkTempsOut));
- fprintf(fout, "Offset / size of bbTryIndex = %3u / %3u\n", offsetof(BasicBlock, bbTryIndex),
- sizeof(bbDummy->bbTryIndex));
- fprintf(fout, "Offset / size of bbHndIndex = %3u / %3u\n", offsetof(BasicBlock, bbHndIndex),
- sizeof(bbDummy->bbHndIndex));
- fprintf(fout, "Offset / size of bbCatchTyp = %3u / %3u\n", offsetof(BasicBlock, bbCatchTyp),
- sizeof(bbDummy->bbCatchTyp));
- fprintf(fout, "Offset / size of bbStkDepth = %3u / %3u\n", offsetof(BasicBlock, bbStkDepth),
- sizeof(bbDummy->bbStkDepth));
- fprintf(fout, "Offset / size of bbFPinVars = %3u / %3u\n", offsetof(BasicBlock, bbFPinVars),
- sizeof(bbDummy->bbFPinVars));
- fprintf(fout, "Offset / size of bbPreds = %3u / %3u\n", offsetof(BasicBlock, bbPreds),
- sizeof(bbDummy->bbPreds));
- fprintf(fout, "Offset / size of bbReach = %3u / %3u\n", offsetof(BasicBlock, bbReach),
- sizeof(bbDummy->bbReach));
- fprintf(fout, "Offset / size of bbIDom = %3u / %3u\n", offsetof(BasicBlock, bbIDom),
- sizeof(bbDummy->bbIDom));
- fprintf(fout, "Offset / size of bbDfsNum = %3u / %3u\n", offsetof(BasicBlock, bbDfsNum),
- sizeof(bbDummy->bbDfsNum));
- fprintf(fout, "Offset / size of bbCodeOffs = %3u / %3u\n", offsetof(BasicBlock, bbCodeOffs),
- sizeof(bbDummy->bbCodeOffs));
- fprintf(fout, "Offset / size of bbCodeOffsEnd = %3u / %3u\n", offsetof(BasicBlock, bbCodeOffsEnd),
- sizeof(bbDummy->bbCodeOffsEnd));
- fprintf(fout, "Offset / size of bbVarUse = %3u / %3u\n", offsetof(BasicBlock, bbVarUse),
- sizeof(bbDummy->bbVarUse));
- fprintf(fout, "Offset / size of bbVarDef = %3u / %3u\n", offsetof(BasicBlock, bbVarDef),
- sizeof(bbDummy->bbVarDef));
- fprintf(fout, "Offset / size of bbLiveIn = %3u / %3u\n", offsetof(BasicBlock, bbLiveIn),
- sizeof(bbDummy->bbLiveIn));
- fprintf(fout, "Offset / size of bbLiveOut = %3u / %3u\n", offsetof(BasicBlock, bbLiveOut),
- sizeof(bbDummy->bbLiveOut));
- fprintf(fout, "Offset / size of bbMemorySsaPhiFunc = %3u / %3u\n", offsetof(BasicBlock, bbMemorySsaPhiFunc),
- sizeof(bbDummy->bbMemorySsaPhiFunc));
- fprintf(fout, "Offset / size of bbMemorySsaNumIn = %3u / %3u\n", offsetof(BasicBlock, bbMemorySsaNumIn),
- sizeof(bbDummy->bbMemorySsaNumIn));
- fprintf(fout, "Offset / size of bbMemorySsaNumOut = %3u / %3u\n", offsetof(BasicBlock, bbMemorySsaNumOut),
- sizeof(bbDummy->bbMemorySsaNumOut));
- fprintf(fout, "Offset / size of bbScope = %3u / %3u\n", offsetof(BasicBlock, bbScope),
- sizeof(bbDummy->bbScope));
- fprintf(fout, "Offset / size of bbCseGen = %3u / %3u\n", offsetof(BasicBlock, bbCseGen),
- sizeof(bbDummy->bbCseGen));
- fprintf(fout, "Offset / size of bbCseIn = %3u / %3u\n", offsetof(BasicBlock, bbCseIn),
- sizeof(bbDummy->bbCseIn));
- fprintf(fout, "Offset / size of bbCseOut = %3u / %3u\n", offsetof(BasicBlock, bbCseOut),
- sizeof(bbDummy->bbCseOut));
-
- fprintf(fout, "Offset / size of bbEmitCookie = %3u / %3u\n", offsetof(BasicBlock, bbEmitCookie),
- sizeof(bbDummy->bbEmitCookie));
-
-#if FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
- fprintf(fout, "Offset / size of bbUnwindNopEmitCookie = %3u / %3u\n", offsetof(BasicBlock, bbUnwindNopEmitCookie),
- sizeof(bbDummy->bbUnwindNopEmitCookie));
-#endif // FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_)
-
-#ifdef VERIFIER
- fprintf(fout, "Offset / size of bbStackIn = %3u / %3u\n", offsetof(BasicBlock, bbStackIn),
- sizeof(bbDummy->bbStackIn));
- fprintf(fout, "Offset / size of bbStackOut = %3u / %3u\n", offsetof(BasicBlock, bbStackOut),
- sizeof(bbDummy->bbStackOut));
- fprintf(fout, "Offset / size of bbTypesIn = %3u / %3u\n", offsetof(BasicBlock, bbTypesIn),
- sizeof(bbDummy->bbTypesIn));
- fprintf(fout, "Offset / size of bbTypesOut = %3u / %3u\n", offsetof(BasicBlock, bbTypesOut),
- sizeof(bbDummy->bbTypesOut));
-#endif // VERIFIER
-
-#ifdef DEBUG
- fprintf(fout, "Offset / size of bbLoopNum = %3u / %3u\n", offsetof(BasicBlock, bbLoopNum),
- sizeof(bbDummy->bbLoopNum));
-#endif // DEBUG
-
- fprintf(fout, "\n");
- fprintf(fout, "Size of BasicBlock = %3u\n", sizeof(BasicBlock));
-
-#endif // MEASURE_BLOCK_SIZE
+ BasicBlock::DisplayStaticSizes(fout);
#if EMITTER_STATS
emitterStaticStats(fout);
@@ -2815,7 +2711,6 @@ void Compiler::compInitOptions(JitFlags* jitFlags)
bool dumpIRBlockHeaders = false;
bool dumpIRExit = false;
LPCWSTR dumpIRPhase = nullptr;
- LPCWSTR dumpIRFormat = nullptr;
if (!altJitConfig || opts.altJit)
{
@@ -7224,17 +7119,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
void codeGeneratorCodeSizeBeg()
{
}
-/*****************************************************************************/
-
-/*****************************************************************************
- *
- * If any temporary tables are smaller than 'genMinSize2free' we won't bother
- * freeing them.
- */
-
-const size_t genMinSize2free = 64;
-
-/*****************************************************************************/
/*****************************************************************************
*
@@ -7647,7 +7531,6 @@ void CompTimeSummaryInfo::Print(FILE* f)
return;
}
- bool extraInfo = (JitConfig.JitEECallTimingInfo() != 0);
double totTime_ms = 0.0;
fprintf(f, "JIT Compilation time report:\n");
@@ -7667,6 +7550,7 @@ void CompTimeSummaryInfo::Print(FILE* f)
const char* extraHdr1 = "";
const char* extraHdr2 = "";
#if MEASURE_CLRAPI_CALLS
+ bool extraInfo = (JitConfig.JitEECallTimingInfo() != 0);
if (extraInfo)
{
extraHdr1 = " CLRs/meth % in CLR";
@@ -7684,9 +7568,8 @@ void CompTimeSummaryInfo::Print(FILE* f)
assert(_countof(PhaseNames) == PHASE_NUMBER_OF);
for (int i = 0; i < PHASE_NUMBER_OF; i++)
{
- double phase_tot_ms = (((double)m_total.m_cyclesByPhase[i]) / countsPerSec) * 1000.0;
- double phase_max_ms = (((double)m_maximum.m_cyclesByPhase[i]) / countsPerSec) * 1000.0;
- double phase_tot_pct = 100.0 * phase_tot_ms / totTime_ms;
+ double phase_tot_ms = (((double)m_total.m_cyclesByPhase[i]) / countsPerSec) * 1000.0;
+ double phase_max_ms = (((double)m_maximum.m_cyclesByPhase[i]) / countsPerSec) * 1000.0;
#if MEASURE_CLRAPI_CALLS
// Skip showing CLR API call info if we didn't collect any
@@ -10993,15 +10876,8 @@ void cNodeIR(Compiler* comp, GenTree* tree)
void cTreeIR(Compiler* comp, GenTree* tree)
{
- bool foldLeafs = comp->dumpIRNoLeafs;
- bool foldIndirs = comp->dumpIRDataflow;
- bool foldLists = comp->dumpIRNoLists;
- bool dataflowView = comp->dumpIRDataflow;
- bool dumpTypes = comp->dumpIRTypes;
- bool dumpValnums = comp->dumpIRValnums;
- bool noStmts = comp->dumpIRNoStmts;
- genTreeOps op = tree->OperGet();
- unsigned childCount = tree->NumChildren();
+ genTreeOps op = tree->OperGet();
+ unsigned childCount = tree->NumChildren();
GenTree* child;
// Recurse and dump trees that this node depends on.
diff --git a/src/jit/compiler.h b/src/jit/compiler.h
index d6edd6483f..eefa345e6e 100644
--- a/src/jit/compiler.h
+++ b/src/jit/compiler.h
@@ -2994,7 +2994,7 @@ public:
unsigned lvaFrameSize(FrameLayoutState curState);
// Returns the caller-SP-relative offset for the SP/FP relative offset determined by FP based.
- int lvaToCallerSPRelativeOffset(int offs, bool isFpBased);
+ int lvaToCallerSPRelativeOffset(int offs, bool isFpBased) const;
// Returns the caller-SP-relative offset for the local variable "varNum."
int lvaGetCallerSPRelativeOffset(unsigned varNum);
@@ -7088,7 +7088,7 @@ public:
// the setter on CodeGenContext directly.
__declspec(property(get = getEmitter)) emitter* genEmitter;
- emitter* getEmitter()
+ emitter* getEmitter() const
{
return codeGen->getEmitter();
}
@@ -8772,9 +8772,9 @@ public:
unsigned compArgSize; // total size of arguments in bytes (including register args (lvIsRegArg))
- unsigned compMapILargNum(unsigned ILargNum); // map accounting for hidden args
- unsigned compMapILvarNum(unsigned ILvarNum); // map accounting for hidden args
- unsigned compMap2ILvarNum(unsigned varNum); // map accounting for hidden args
+ unsigned compMapILargNum(unsigned ILargNum); // map accounting for hidden args
+ unsigned compMapILvarNum(unsigned ILvarNum); // map accounting for hidden args
+ unsigned compMap2ILvarNum(unsigned varNum) const; // map accounting for hidden args
//-------------------------------------------------------------------------
@@ -8844,7 +8844,7 @@ public:
#endif // LOOP_HOIST_STATS
bool compIsForImportOnly();
- bool compIsForInlining();
+ bool compIsForInlining() const;
bool compDonotInline();
#ifdef DEBUG
diff --git a/src/jit/compiler.hpp b/src/jit/compiler.hpp
index c1be6acbbe..2eb5ac7b08 100644
--- a/src/jit/compiler.hpp
+++ b/src/jit/compiler.hpp
@@ -3859,7 +3859,7 @@ inline bool Compiler::compIsForImportOnly()
* Returns true if the compiler instance is created for inlining.
*/
-inline bool Compiler::compIsForInlining()
+inline bool Compiler::compIsForInlining() const
{
return (impInlineInfo != nullptr);
}
diff --git a/src/jit/earlyprop.cpp b/src/jit/earlyprop.cpp
index 26a50d894b..bd4c222664 100644
--- a/src/jit/earlyprop.cpp
+++ b/src/jit/earlyprop.cpp
@@ -322,7 +322,6 @@ GenTree* Compiler::optEarlyPropRewriteTree(GenTree* tree)
GenTree* comma = check->gtGetParent(nullptr);
if ((comma != nullptr) && comma->OperIs(GT_COMMA) && (comma->gtGetOp1() == check))
{
- GenTree* next = check->gtNext;
optRemoveRangeCheck(comma, compCurStmt);
// Both `tree` and `check` have been removed from the statement.
// 'tree' was replaced with 'nop' or side effect list under 'comma'.
@@ -497,11 +496,11 @@ void Compiler::optFoldNullCheck(GenTree* tree)
// Check for a pattern like this:
//
// =
- // / \
+ // / \.
// x comma
- // / \
+ // / \.
// nullcheck +
- // | / \
+ // | / \.
// y y const
//
//
@@ -517,9 +516,9 @@ void Compiler::optFoldNullCheck(GenTree* tree)
// and transform it into
//
// =
- // / \
+ // / \.
// x +
- // / \
+ // / \.
// y const
//
//
diff --git a/src/jit/ee_il_dll.cpp b/src/jit/ee_il_dll.cpp
index 410ef6ca01..ca3fce2aad 100644
--- a/src/jit/ee_il_dll.cpp
+++ b/src/jit/ee_il_dll.cpp
@@ -163,10 +163,11 @@ void jitShutdown(bool processIsTerminating)
#ifndef FEATURE_MERGE_JIT_AND_ENGINE
+extern "C"
#ifdef FEATURE_PAL
-DLLEXPORT // For Win32 PAL LoadLibrary emulation
+ DLLEXPORT // For Win32 PAL LoadLibrary emulation
#endif
- extern "C" BOOL WINAPI
+ BOOL WINAPI
DllMain(HANDLE hInstance, DWORD dwReason, LPVOID pvReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
@@ -230,7 +231,11 @@ DLLEXPORT ICorJitCompiler* __stdcall getJit()
// If you are using it more broadly in retail code, you would need to understand the
// performance implications of accessing TLS.
+#ifndef __GNUC__
__declspec(thread) void* gJitTls = nullptr;
+#else // !__GNUC__
+thread_local void* gJitTls = nullptr;
+#endif // !__GNUC__
static void* GetJitTls()
{
diff --git a/src/jit/emit.cpp b/src/jit/emit.cpp
index 982417d9d6..daaf237849 100644
--- a/src/jit/emit.cpp
+++ b/src/jit/emit.cpp
@@ -267,6 +267,10 @@ size_t emitter::emitSizeMethod;
size_t emitter::emitTotMemAlloc;
unsigned emitter::emitTotalInsCnt;
+unsigned emitter::emitCurPrologInsCnt;
+size_t emitter::emitCurPrologIGSize;
+unsigned emitter::emitMaxPrologInsCnt;
+size_t emitter::emitMaxPrologIGSize;
unsigned emitter::emitTotalIGcnt;
unsigned emitter::emitTotalPhIGcnt;
unsigned emitter::emitTotalIGjmps;
@@ -274,6 +278,25 @@ unsigned emitter::emitTotalIGptrs;
unsigned emitter::emitTotalIGicnt;
size_t emitter::emitTotalIGsize;
unsigned emitter::emitTotalIGmcnt;
+unsigned emitter::emitTotalIGEmitAdd;
+
+unsigned emitter::emitTotalIDescSmallCnt;
+unsigned emitter::emitTotalIDescCnt;
+unsigned emitter::emitTotalIDescJmpCnt;
+#if !defined(_TARGET_ARM64_)
+unsigned emitter::emitTotalIDescLblCnt;
+#endif // !defined(_TARGET_ARM64_)
+unsigned emitter::emitTotalIDescCnsCnt;
+unsigned emitter::emitTotalIDescDspCnt;
+unsigned emitter::emitTotalIDescCnsDspCnt;
+#ifdef _TARGET_XARCH_
+unsigned emitter::emitTotalIDescAmdCnt;
+unsigned emitter::emitTotalIDescCnsAmdCnt;
+#endif // _TARGET_XARCH_
+unsigned emitter::emitTotalIDescCGCACnt;
+#ifdef _TARGET_ARM_
+unsigned emitter::emitTotalIDescRelocCnt;
+#endif // _TARGET_ARM_
unsigned emitter::emitSmallDspCnt;
unsigned emitter::emitLargeDspCnt;
@@ -286,24 +309,40 @@ void emitterStaticStats(FILE* fout)
{
// insGroup members
+ insGroup* igDummy = nullptr;
+
fprintf(fout, "\n");
fprintf(fout, "insGroup:\n");
- fprintf(fout, "Offset of igNext = %2u\n", offsetof(insGroup, igNext));
+ fprintf(fout, "Offset / size of igNext = %2u / %2u\n", offsetof(insGroup, igNext),
+ sizeof(igDummy->igNext));
#ifdef DEBUG
- fprintf(fout, "Offset of igSelf = %2u\n", offsetof(insGroup, igSelf));
+ fprintf(fout, "Offset / size of igSelf = %2u / %2u\n", offsetof(insGroup, igSelf),
+ sizeof(igDummy->igSelf));
#endif
- fprintf(fout, "Offset of igNum = %2u\n", offsetof(insGroup, igNum));
- fprintf(fout, "Offset of igOffs = %2u\n", offsetof(insGroup, igOffs));
- fprintf(fout, "Offset of igFuncIdx = %2u\n", offsetof(insGroup, igFuncIdx));
- fprintf(fout, "Offset of igFlags = %2u\n", offsetof(insGroup, igFlags));
- fprintf(fout, "Offset of igSize = %2u\n", offsetof(insGroup, igSize));
- fprintf(fout, "Offset of igData = %2u\n", offsetof(insGroup, igData));
+ fprintf(fout, "Offset / size of igNum = %2u / %2u\n", offsetof(insGroup, igNum),
+ sizeof(igDummy->igNum));
+ fprintf(fout, "Offset / size of igOffs = %2u / %2u\n", offsetof(insGroup, igOffs),
+ sizeof(igDummy->igOffs));
+ fprintf(fout, "Offset / size of igFuncIdx = %2u / %2u\n", offsetof(insGroup, igFuncIdx),
+ sizeof(igDummy->igFuncIdx));
+ fprintf(fout, "Offset / size of igFlags = %2u / %2u\n", offsetof(insGroup, igFlags),
+ sizeof(igDummy->igFlags));
+ fprintf(fout, "Offset / size of igSize = %2u / %2u\n", offsetof(insGroup, igSize),
+ sizeof(igDummy->igSize));
+ fprintf(fout, "Offset / size of igData = %2u / %2u\n", offsetof(insGroup, igData),
+ sizeof(igDummy->igData));
+ fprintf(fout, "Offset / size of igPhData = %2u / %2u\n", offsetof(insGroup, igPhData),
+ sizeof(igDummy->igPhData));
#if EMIT_TRACK_STACK_DEPTH
- fprintf(fout, "Offset of igStkLvl = %2u\n", offsetof(insGroup, igStkLvl));
+ fprintf(fout, "Offset / size of igStkLvl = %2u / %2u\n", offsetof(insGroup, igStkLvl),
+ sizeof(igDummy->igStkLvl));
#endif
- fprintf(fout, "Offset of igGCregs = %2u\n", offsetof(insGroup, igGCregs));
- fprintf(fout, "Offset of igInsCnt = %2u\n", offsetof(insGroup, igInsCnt));
- fprintf(fout, "Size of insGroup = %u\n", sizeof(insGroup));
+ fprintf(fout, "Offset / size of igGCregs = %2u / %2u\n", offsetof(insGroup, igGCregs),
+ sizeof(igDummy->igGCregs));
+ fprintf(fout, "Offset / size of igInsCnt = %2u / %2u\n", offsetof(insGroup, igInsCnt),
+ sizeof(igDummy->igInsCnt));
+ fprintf(fout, "\n");
+ fprintf(fout, "Size of insGroup = %u\n", sizeof(insGroup));
// insPlaceholderGroupData members
@@ -321,7 +360,8 @@ void emitterStaticStats(FILE* fout)
fprintf(fout, "Size of insPlaceholderGroupData = %u\n", sizeof(insPlaceholderGroupData));
fprintf(fout, "\n");
- fprintf(fout, "Size of instrDesc = %2u\n", sizeof(emitter::instrDesc));
+ fprintf(fout, "SMALL_IDSC_SIZE = %2u\n", SMALL_IDSC_SIZE);
+ fprintf(fout, "Size of instrDesc = %2u\n", sizeof(emitter::instrDesc));
// fprintf(fout, "Offset of _idIns = %2u\n", offsetof(emitter::instrDesc, _idIns ));
// fprintf(fout, "Offset of _idInsFmt = %2u\n", offsetof(emitter::instrDesc, _idInsFmt ));
// fprintf(fout, "Offset of _idOpSize = %2u\n", offsetof(emitter::instrDesc, _idOpSize ));
@@ -330,6 +370,42 @@ void emitterStaticStats(FILE* fout)
// fprintf(fout, "\n");
// fprintf(fout, "Size of _idAddrUnion= %2u\n", sizeof(((emitter::instrDesc*)0)->_idAddrUnion));
+ fprintf(fout, "Size of instrDescJmp = %2u\n", sizeof(emitter::instrDescJmp));
+#if !defined(_TARGET_ARM64_)
+ fprintf(fout, "Size of instrDescLbl = %2u\n", sizeof(emitter::instrDescLbl));
+#endif // !defined(_TARGET_ARM64_)
+ fprintf(fout, "Size of instrDescCns = %2u\n", sizeof(emitter::instrDescCns));
+ fprintf(fout, "Size of instrDescDsp = %2u\n", sizeof(emitter::instrDescDsp));
+ fprintf(fout, "Size of instrDescCnsDsp = %2u\n", sizeof(emitter::instrDescCnsDsp));
+#ifdef _TARGET_XARCH_
+ fprintf(fout, "Size of instrDescAmd = %2u\n", sizeof(emitter::instrDescAmd));
+ fprintf(fout, "Size of instrDescCnsAmd = %2u\n", sizeof(emitter::instrDescCnsAmd));
+#endif // _TARGET_XARCH_
+ fprintf(fout, "Size of instrDescCGCA = %2u\n", sizeof(emitter::instrDescCGCA));
+#ifdef _TARGET_ARM_
+ fprintf(fout, "Size of instrDescReloc = %2u\n", sizeof(emitter::instrDescReloc));
+#endif // _TARGET_ARM_
+
+ fprintf(fout, "\n");
+ fprintf(fout, "SC_IG_BUFFER_SIZE = %2u\n", SC_IG_BUFFER_SIZE);
+ fprintf(fout, "SMALL_IDSC_SIZE per IG buffer = %2u\n", SC_IG_BUFFER_SIZE / SMALL_IDSC_SIZE);
+ fprintf(fout, "instrDesc per IG buffer = %2u\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDesc));
+ fprintf(fout, "instrDescJmp per IG buffer = %2u\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescJmp));
+#if !defined(_TARGET_ARM64_)
+ fprintf(fout, "instrDescLbl per IG buffer = %2u\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescLbl));
+#endif // !defined(_TARGET_ARM64_)
+ fprintf(fout, "instrDescCns per IG buffer = %2u\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescCns));
+ fprintf(fout, "instrDescDsp per IG buffer = %2u\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescDsp));
+ fprintf(fout, "instrDescCnsDsp per IG buffer = %2u\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescCnsDsp));
+#ifdef _TARGET_XARCH_
+ fprintf(fout, "instrDescAmd per IG buffer = %2u\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescAmd));
+ fprintf(fout, "instrDescCnsAmd per IG buffer = %2u\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescCnsAmd));
+#endif // _TARGET_XARCH_
+ fprintf(fout, "instrDescCGCA per IG buffer = %2u\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescCGCA));
+#ifdef _TARGET_ARM_
+ fprintf(fout, "instrDescReloc per IG buffer = %2u\n", SC_IG_BUFFER_SIZE / sizeof(emitter::instrDescReloc));
+#endif // _TARGET_ARM_
+
fprintf(fout, "\n");
fprintf(fout, "GCInfo::regPtrDsc:\n");
fprintf(fout, "Offset of rpdNext = %2u\n", offsetof(GCInfo::regPtrDsc, rpdNext));
@@ -356,7 +432,7 @@ void emitterStats(FILE* fout)
fprintf(fout, "\n");
}
- assert(emitter::emitTotalInsCnt);
+ assert(emitter::emitTotalInsCnt > 0);
fprintf(fout, "Average of %4.2f bytes of code generated per instruction\n",
(double)totActualSize / emitter::emitTotalInsCnt);
@@ -378,26 +454,33 @@ void emitterStats(FILE* fout)
if ((c > 0) && (1000 * c >= ic))
{
dc += c;
- fprintf(fout, " %-13s %8u (%5.2f%%)\n", emitter::emitIfName(f), c, 100.0 * c / ic);
+ fprintf(fout, " %-14s %8u (%5.2f%%)\n", emitter::emitIfName(f), c, 100.0 * c / ic);
}
}
- fprintf(fout, " --------------------------------\n");
- fprintf(fout, " %-13s %8u (%5.2f%%)\n", "Total shown", dc, 100.0 * dc / ic);
+ fprintf(fout, " ---------------------------------\n");
+ fprintf(fout, " %-14s %8u (%5.2f%%)\n", "Total shown", dc, 100.0 * dc / ic);
- if (emitter::emitTotalIGmcnt)
+ if (emitter::emitTotalIGmcnt > 0)
{
+ fprintf(fout, "\n");
fprintf(fout, "Total of %8u methods\n", emitter::emitTotalIGmcnt);
fprintf(fout, "Total of %8u insGroup\n", emitter::emitTotalIGcnt);
fprintf(fout, "Total of %8u insPlaceholderGroupData\n", emitter::emitTotalPhIGcnt);
+ fprintf(fout, "Total of %8u emitAdd insGroup\n", emitter::emitTotalIGEmitAdd);
fprintf(fout, "Total of %8u instructions\n", emitter::emitTotalIGicnt);
fprintf(fout, "Total of %8u jumps\n", emitter::emitTotalIGjmps);
fprintf(fout, "Total of %8u GC livesets\n", emitter::emitTotalIGptrs);
fprintf(fout, "\n");
+ fprintf(fout, "Max prolog instrDesc count: %8u\n", emitter::emitMaxPrologInsCnt);
+ fprintf(fout, "Max prolog insGroup size : %8u\n", emitter::emitMaxPrologIGSize);
+ fprintf(fout, "\n");
fprintf(fout, "Average of %8.1lf insGroup per method\n",
(double)emitter::emitTotalIGcnt / emitter::emitTotalIGmcnt);
fprintf(fout, "Average of %8.1lf insPhGroup per method\n",
(double)emitter::emitTotalPhIGcnt / emitter::emitTotalIGmcnt);
+ fprintf(fout, "Average of %8.1lf emitAdd IG per method\n",
+ (double)emitter::emitTotalIGEmitAdd / emitter::emitTotalIGmcnt);
fprintf(fout, "Average of %8.1lf instructions per method\n",
(double)emitter::emitTotalIGicnt / emitter::emitTotalIGmcnt);
fprintf(fout, "Average of %8.1lf desc. bytes per method\n",
@@ -419,6 +502,37 @@ void emitterStats(FILE* fout)
fprintf(fout, "\n");
fprintf(fout, "A total of %8u desc. bytes\n", emitter::emitTotalIGsize);
fprintf(fout, "\n");
+
+ fprintf(fout, "Total instructions: %8u\n", emitter::emitTotalInsCnt);
+ fprintf(fout, "Total small instrDesc: %8u (%5.2f%%)\n", emitter::emitTotalIDescSmallCnt,
+ 100.0 * emitter::emitTotalIDescSmallCnt / emitter::emitTotalInsCnt);
+ fprintf(fout, "Total instrDesc: %8u (%5.2f%%)\n", emitter::emitTotalIDescCnt,
+ 100.0 * emitter::emitTotalIDescCnt / emitter::emitTotalInsCnt);
+ fprintf(fout, "Total instrDescJmp: %8u (%5.2f%%)\n", emitter::emitTotalIDescJmpCnt,
+ 100.0 * emitter::emitTotalIDescJmpCnt / emitter::emitTotalInsCnt);
+#if !defined(_TARGET_ARM64_)
+ fprintf(fout, "Total instrDescLbl: %8u (%5.2f%%)\n", emitter::emitTotalIDescLblCnt,
+ 100.0 * emitter::emitTotalIDescLblCnt / emitter::emitTotalInsCnt);
+#endif // !defined(_TARGET_ARM64_)
+ fprintf(fout, "Total instrDescCns: %8u (%5.2f%%)\n", emitter::emitTotalIDescCnsCnt,
+ 100.0 * emitter::emitTotalIDescCnsCnt / emitter::emitTotalInsCnt);
+ fprintf(fout, "Total instrDescDsp: %8u (%5.2f%%)\n", emitter::emitTotalIDescDspCnt,
+ 100.0 * emitter::emitTotalIDescDspCnt / emitter::emitTotalInsCnt);
+ fprintf(fout, "Total instrDescCnsDsp: %8u (%5.2f%%)\n", emitter::emitTotalIDescCnsDspCnt,
+ 100.0 * emitter::emitTotalIDescCnsDspCnt / emitter::emitTotalInsCnt);
+#ifdef _TARGET_XARCH_
+ fprintf(fout, "Total instrDescAmd: %8u (%5.2f%%)\n", emitter::emitTotalIDescAmdCnt,
+ 100.0 * emitter::emitTotalIDescAmdCnt / emitter::emitTotalInsCnt);
+ fprintf(fout, "Total instrDescCnsAmd: %8u (%5.2f%%)\n", emitter::emitTotalIDescCnsAmdCnt,
+ 100.0 * emitter::emitTotalIDescCnsAmdCnt / emitter::emitTotalInsCnt);
+#endif // _TARGET_XARCH_
+ fprintf(fout, "Total instrDescCGCA: %8u (%5.2f%%)\n", emitter::emitTotalIDescCGCACnt,
+ 100.0 * emitter::emitTotalIDescCGCACnt / emitter::emitTotalInsCnt);
+#ifdef _TARGET_ARM_
+ fprintf(fout, "Total instrDescReloc: %8u (%5.2f%%)\n", emitter::emitTotalIDescRelocCnt,
+ 100.0 * emitter::emitTotalIDescRelocCnt / emitter::emitTotalInsCnt);
+#endif // _TARGET_ARM_
+ fprintf(fout, "\n");
}
fprintf(fout, "Descriptor size distribution:\n");
@@ -433,33 +547,37 @@ void emitterStats(FILE* fout)
stkDepthTable.dump(fout);
fprintf(fout, "\n");
- int i;
- unsigned c;
- unsigned m;
-
- if (emitter::emitSmallCnsCnt || emitter::emitLargeCnsCnt)
+ if ((emitter::emitSmallCnsCnt > 0) || (emitter::emitLargeCnsCnt > 0))
{
fprintf(fout, "SmallCnsCnt = %6u\n", emitter::emitSmallCnsCnt);
fprintf(fout, "LargeCnsCnt = %6u (%3u %% of total)\n", emitter::emitLargeCnsCnt,
100 * emitter::emitLargeCnsCnt / (emitter::emitLargeCnsCnt + emitter::emitSmallCnsCnt));
}
-#if 0
- // TODO-Cleanup: WHy is this in #if 0 - Is EMITTER_STATS ever used? Fix or delete this.
- if (emitter::emitSmallCnsCnt)
+ // Print out the most common small constants.
+ if (emitter::emitSmallCnsCnt > 0)
{
- fprintf(fout, "\n");
+ fprintf(fout, "\n\n");
+ fprintf(fout, "Common small constants >= %2u, <= %2u\n", ID_MIN_SMALL_CNS, ID_MAX_SMALL_CNS);
- m = emitter::emitSmallCnsCnt/1000 + 1;
+ unsigned m = emitter::emitSmallCnsCnt / 1000 + 1;
- for (i = ID_MIN_SMALL_CNS; i < ID_MAX_SMALL_CNS; i++)
+ for (int i = ID_MIN_SMALL_CNS; (i <= ID_MAX_SMALL_CNS) && (i < SMALL_CNS_TSZ); i++)
{
- c = emitter::emitSmallCns[i-ID_MIN_SMALL_CNS];
- if (c >= m)
- fprintf(fout, "cns[%4d] = %u\n", i, c);
+ unsigned c = emitter::emitSmallCns[i - ID_MIN_SMALL_CNS];
+ if (c >= m)
+ {
+ if (i == SMALL_CNS_TSZ - 1)
+ {
+ fprintf(fout, "cns[>=%4d] = %u\n", i, c);
+ }
+ else
+ {
+ fprintf(fout, "cns[%4d] = %u\n", i, c);
+ }
+ }
}
}
-#endif // 0
fprintf(fout, "%8u bytes allocated in the emitter\n", emitter::emitTotMemAlloc);
}
@@ -789,6 +907,22 @@ insGroup* emitter::emitSavIG(bool emitAdd)
emitTotalIGicnt += emitCurIGinsCnt;
emitTotalIGsize += sz;
emitSizeMethod += sz;
+
+ if (emitIGisInProlog(ig))
+ {
+ emitCurPrologInsCnt += emitCurIGinsCnt;
+ emitCurPrologIGSize += sz;
+
+ // Keep track of the maximums.
+ if (emitCurPrologInsCnt > emitMaxPrologInsCnt)
+ {
+ emitMaxPrologInsCnt = emitCurPrologInsCnt;
+ }
+ if (emitCurPrologIGSize > emitMaxPrologIGSize)
+ {
+ emitMaxPrologIGSize = emitCurPrologIGSize;
+ }
+ }
#endif
// printf("Group [%08X]%3u has %2u instructions (%4u bytes at %08X)\n", ig, ig->igNum, emitCurIGinsCnt, sz, id);
@@ -1019,7 +1153,9 @@ void emitter::emitBegFN(bool hasFramePtr
#if EMITTER_STATS
emitTotalIGmcnt++;
- emitSizeMethod = 0;
+ emitSizeMethod = 0;
+ emitCurPrologInsCnt = 0;
+ emitCurPrologIGSize = 0;
#endif
emitInsCount = 0;
@@ -1170,7 +1306,7 @@ size_t emitter::emitGenEpilogLst(size_t (*fp)(void*, unsigned), void* cp)
* The following series of methods allocates instruction descriptors.
*/
-void* emitter::emitAllocInstr(size_t sz, emitAttr opsz)
+void* emitter::emitAllocAnyInstr(size_t sz, emitAttr opsz)
{
instrDesc* id;
@@ -2154,7 +2290,10 @@ emitter::instrDesc* emitter::emitNewInstrCnsDsp(emitAttr size, target_ssize_t cn
#if EMITTER_STATS
emitSmallCnsCnt++;
- emitSmallCns[cns - ID_MIN_SMALL_CNS]++;
+ if ((cns - ID_MIN_SMALL_CNS) >= (SMALL_CNS_TSZ - 1))
+ emitSmallCns[SMALL_CNS_TSZ - 1]++;
+ else
+ emitSmallCns[cns - ID_MIN_SMALL_CNS]++;
emitSmallDspCnt++;
#endif
@@ -2162,10 +2301,7 @@ emitter::instrDesc* emitter::emitNewInstrCnsDsp(emitAttr size, target_ssize_t cn
}
else
{
- instrDescCns* id = emitAllocInstrCns(size);
-
- id->idSetIsLargeCns();
- id->idcCnsVal = cns;
+ instrDescCns* id = emitAllocInstrCns(size, cns);
#if EMITTER_STATS
emitLargeCnsCnt++;
@@ -2189,7 +2325,10 @@ emitter::instrDesc* emitter::emitNewInstrCnsDsp(emitAttr size, target_ssize_t cn
#if EMITTER_STATS
emitLargeDspCnt++;
emitSmallCnsCnt++;
- emitSmallCns[cns - ID_MIN_SMALL_CNS]++;
+ if ((cns - ID_MIN_SMALL_CNS) >= (SMALL_CNS_TSZ - 1))
+ emitSmallCns[SMALL_CNS_TSZ - 1]++;
+ else
+ emitSmallCns[cns - ID_MIN_SMALL_CNS]++;
#endif
return id;
@@ -6861,6 +7000,10 @@ void emitter::emitNxtIG(bool emitAdd)
if (emitAdd)
{
emitCurIG->igFlags |= IGF_EMIT_ADD;
+
+#if EMITTER_STATS
+ emitTotalIGEmitAdd++;
+#endif // EMITTER_STATS
}
// We've created a new IG; no need to force another one.
diff --git a/src/jit/emit.h b/src/jit/emit.h
index 876dc085fc..1308531a90 100644
--- a/src/jit/emit.h
+++ b/src/jit/emit.h
@@ -153,7 +153,7 @@ public:
// A constructor for code that needs to call it explicitly.
void Init()
{
- this->emitLocation::emitLocation();
+ *this = emitLocation();
}
void CaptureLocation(emitter* emit);
@@ -589,7 +589,8 @@ protected:
static_assert_no_msg(INS_count <= 256);
instruction _idIns : 8;
#endif // !(defined(_TARGET_XARCH_) || defined(_TARGET_ARM64_))
- // The format for the instruction
+
+// The format for the instruction
#if defined(_TARGET_XARCH_)
static_assert_no_msg(IF_COUNT <= 128);
insFormat _idInsFmt : 7;
@@ -1613,9 +1614,9 @@ private:
// and must store them all to the frame on entry. If the frame is very large, we generate
// ugly code like "movw r10, 0x488; add r10, sp; vstr s0, [r10]" for each store, which
// eats up our insGroup buffer.
-#define SC_IG_BUFFER_SIZE (100 * sizeof(instrDesc) + 14 * SMALL_IDSC_SIZE)
+#define SC_IG_BUFFER_SIZE (100 * sizeof(emitter::instrDesc) + 14 * SMALL_IDSC_SIZE)
#else // !_TARGET_ARMARCH_
-#define SC_IG_BUFFER_SIZE (50 * sizeof(instrDesc) + 14 * SMALL_IDSC_SIZE)
+#define SC_IG_BUFFER_SIZE (50 * sizeof(emitter::instrDesc) + 14 * SMALL_IDSC_SIZE)
#endif // !_TARGET_ARMARCH_
size_t emitIGbuffSize;
@@ -1771,32 +1772,52 @@ private:
int emitNextRandomNop();
- void* emitAllocInstr(size_t sz, emitAttr attr);
+ //
+ // Functions for allocating instrDescs.
+ //
+ // The emitAllocXXX functions are the base level that allocate memory, and do little else.
+ // The emitters themselves use emitNewXXX, which might be thin wrappers over the emitAllocXXX functions.
+ //
+
+ void* emitAllocAnyInstr(size_t sz, emitAttr attr);
instrDesc* emitAllocInstr(emitAttr attr)
{
- return (instrDesc*)emitAllocInstr(sizeof(instrDesc), attr);
+#if EMITTER_STATS
+ emitTotalIDescCnt++;
+#endif // EMITTER_STATS
+ return (instrDesc*)emitAllocAnyInstr(sizeof(instrDesc), attr);
}
instrDescJmp* emitAllocInstrJmp()
{
- return (instrDescJmp*)emitAllocInstr(sizeof(instrDescJmp), EA_1BYTE);
+#if EMITTER_STATS
+ emitTotalIDescJmpCnt++;
+#endif // EMITTER_STATS
+ return (instrDescJmp*)emitAllocAnyInstr(sizeof(instrDescJmp), EA_1BYTE);
}
#if !defined(_TARGET_ARM64_)
instrDescLbl* emitAllocInstrLbl()
{
- return (instrDescLbl*)emitAllocInstr(sizeof(instrDescLbl), EA_4BYTE);
+#if EMITTER_STATS
+ emitTotalIDescLblCnt++;
+#endif // EMITTER_STATS
+ return (instrDescLbl*)emitAllocAnyInstr(sizeof(instrDescLbl), EA_4BYTE);
}
#endif // !_TARGET_ARM64_
instrDescCns* emitAllocInstrCns(emitAttr attr)
{
- return (instrDescCns*)emitAllocInstr(sizeof(instrDescCns), attr);
+#if EMITTER_STATS
+ emitTotalIDescCnsCnt++;
+#endif // EMITTER_STATS
+ return (instrDescCns*)emitAllocAnyInstr(sizeof(instrDescCns), attr);
}
- instrDescCns* emitAllocInstrCns(emitAttr attr, int cns)
+
+ instrDescCns* emitAllocInstrCns(emitAttr attr, target_size_t cns)
{
- instrDescCns* result = (instrDescCns*)emitAllocInstr(sizeof(instrDescCns), attr);
+ instrDescCns* result = emitAllocInstrCns(attr);
result->idSetIsLargeCns();
result->idcCnsVal = cns;
return result;
@@ -1804,31 +1825,46 @@ private:
instrDescDsp* emitAllocInstrDsp(emitAttr attr)
{
- return (instrDescDsp*)emitAllocInstr(sizeof(instrDescDsp), attr);
+#if EMITTER_STATS
+ emitTotalIDescDspCnt++;
+#endif // EMITTER_STATS
+ return (instrDescDsp*)emitAllocAnyInstr(sizeof(instrDescDsp), attr);
}
instrDescCnsDsp* emitAllocInstrCnsDsp(emitAttr attr)
{
- return (instrDescCnsDsp*)emitAllocInstr(sizeof(instrDescCnsDsp), attr);
+#if EMITTER_STATS
+ emitTotalIDescCnsDspCnt++;
+#endif // EMITTER_STATS
+ return (instrDescCnsDsp*)emitAllocAnyInstr(sizeof(instrDescCnsDsp), attr);
}
#ifdef _TARGET_XARCH_
instrDescAmd* emitAllocInstrAmd(emitAttr attr)
{
- return (instrDescAmd*)emitAllocInstr(sizeof(instrDescAmd), attr);
+#if EMITTER_STATS
+ emitTotalIDescAmdCnt++;
+#endif // EMITTER_STATS
+ return (instrDescAmd*)emitAllocAnyInstr(sizeof(instrDescAmd), attr);
}
instrDescCnsAmd* emitAllocInstrCnsAmd(emitAttr attr)
{
- return (instrDescCnsAmd*)emitAllocInstr(sizeof(instrDescCnsAmd), attr);
+#if EMITTER_STATS
+ emitTotalIDescCnsAmdCnt++;
+#endif // EMITTER_STATS
+ return (instrDescCnsAmd*)emitAllocAnyInstr(sizeof(instrDescCnsAmd), attr);
}
#endif // _TARGET_XARCH_
instrDescCGCA* emitAllocInstrCGCA(emitAttr attr)
{
- return (instrDescCGCA*)emitAllocInstr(sizeof(instrDescCGCA), attr);
+#if EMITTER_STATS
+ emitTotalIDescCGCACnt++;
+#endif // EMITTER_STATS
+ return (instrDescCGCA*)emitAllocAnyInstr(sizeof(instrDescCGCA), attr);
}
instrDesc* emitNewInstrSmall(emitAttr attr);
@@ -2103,14 +2139,38 @@ public:
static unsigned emitTotalInsCnt;
+ static unsigned emitCurPrologInsCnt; // current number of prolog instrDescs
+ static size_t emitCurPrologIGSize; // current size of prolog instrDescs
+ static unsigned emitMaxPrologInsCnt; // maximum number of prolog instrDescs
+ static size_t emitMaxPrologIGSize; // maximum size of prolog instrDescs
+
static unsigned emitTotalIGcnt; // total number of insGroup allocated
static unsigned emitTotalPhIGcnt; // total number of insPlaceholderGroupData allocated
static unsigned emitTotalIGicnt;
static size_t emitTotalIGsize;
- static unsigned emitTotalIGmcnt; // total method count
+ static unsigned emitTotalIGmcnt; // total method count
+ static unsigned emitTotalIGEmitAdd; // total number of 'emitAdd' (overflow) groups
static unsigned emitTotalIGjmps;
static unsigned emitTotalIGptrs;
+ static unsigned emitTotalIDescSmallCnt;
+ static unsigned emitTotalIDescCnt;
+ static unsigned emitTotalIDescJmpCnt;
+#if !defined(_TARGET_ARM64_)
+ static unsigned emitTotalIDescLblCnt;
+#endif // !defined(_TARGET_ARM64_)
+ static unsigned emitTotalIDescCnsCnt;
+ static unsigned emitTotalIDescDspCnt;
+ static unsigned emitTotalIDescCnsDspCnt;
+#ifdef _TARGET_XARCH_
+ static unsigned emitTotalIDescAmdCnt;
+ static unsigned emitTotalIDescCnsAmdCnt;
+#endif // _TARGET_XARCH_
+ static unsigned emitTotalIDescCGCACnt;
+#ifdef _TARGET_ARM_
+ static unsigned emitTotalIDescRelocCnt;
+#endif // _TARGET_ARM_
+
static size_t emitTotMemAlloc;
static unsigned emitSmallDspCnt;
@@ -2294,10 +2354,13 @@ inline emitter::instrDesc* emitter::emitNewInstrSmall(emitAttr attr)
{
instrDesc* id;
- // This is larger than the Tiny Descr
- id = (instrDesc*)emitAllocInstr(SMALL_IDSC_SIZE, attr);
+ id = (instrDesc*)emitAllocAnyInstr(SMALL_IDSC_SIZE, attr);
id->idSetIsSmallDsc();
+#if EMITTER_STATS
+ emitTotalIDescSmallCnt++;
+#endif // EMITTER_STATS
+
return id;
}
@@ -2359,12 +2422,11 @@ inline emitter::instrDesc* emitter::emitNewInstrCns(emitAttr attr, target_ssize_
if (instrDesc::fitsInSmallCns(cns))
{
instrDesc* id = emitAllocInstr(attr);
-
id->idSmallCns(cns);
#if EMITTER_STATS
emitSmallCnsCnt++;
- if (cns - ID_MIN_SMALL_CNS >= SMALL_CNS_TSZ)
+ if ((cns - ID_MIN_SMALL_CNS) >= (SMALL_CNS_TSZ - 1))
emitSmallCns[SMALL_CNS_TSZ - 1]++;
else
emitSmallCns[cns - ID_MIN_SMALL_CNS]++;
@@ -2374,10 +2436,7 @@ inline emitter::instrDesc* emitter::emitNewInstrCns(emitAttr attr, target_ssize_
}
else
{
- instrDescCns* id = emitAllocInstrCns(attr);
-
- id->idSetIsLargeCns();
- id->idcCnsVal = cns;
+ instrDescCns* id = emitAllocInstrCns(attr, cns);
#if EMITTER_STATS
emitLargeCnsCnt++;
@@ -2414,29 +2473,36 @@ inline size_t emitter::emitGetInstrDescSize(const instrDesc* id)
* constant operand. This is the same as emitNewInstrCns() except that here
* any constant that is small enough for instrDesc::fitsInSmallCns() only gets
* allocated SMALL_IDSC_SIZE bytes (and is thus a small descriptor, whereas
- * emitNewInstrCns() always allocates at least sizeof(instrDesc).
+ * emitNewInstrCns() always allocates at least sizeof(instrDesc)).
*/
inline emitter::instrDesc* emitter::emitNewInstrSC(emitAttr attr, target_ssize_t cns)
{
- instrDesc* id;
-
if (instrDesc::fitsInSmallCns(cns))
{
- id = (instrDesc*)emitAllocInstr(SMALL_IDSC_SIZE, attr);
-
+ instrDesc* id = emitNewInstrSmall(attr);
id->idSmallCns(cns);
- id->idSetIsSmallDsc();
+
+#if EMITTER_STATS
+ emitSmallCnsCnt++;
+ if ((cns - ID_MIN_SMALL_CNS) >= (SMALL_CNS_TSZ - 1))
+ emitSmallCns[SMALL_CNS_TSZ - 1]++;
+ else
+ emitSmallCns[cns - ID_MIN_SMALL_CNS]++;
+#endif
+
+ return id;
}
else
{
- id = (instrDesc*)emitAllocInstr(sizeof(instrDescCns), attr);
+ instrDescCns* id = emitAllocInstrCns(attr, cns);
- id->idSetIsLargeCns();
- ((instrDescCns*)id)->idcCnsVal = cns;
- }
+#if EMITTER_STATS
+ emitLargeCnsCnt++;
+#endif
- return id;
+ return id;
+ }
}
/*****************************************************************************
@@ -2466,11 +2532,15 @@ inline emitter::instrDesc* emitter::emitNewInstrReloc(emitAttr attr, BYTE* addr)
{
assert(EA_IS_RELOC(attr));
- instrDescReloc* id = (instrDescReloc*)emitAllocInstr(sizeof(instrDescReloc), attr);
+ instrDescReloc* id = (instrDescReloc*)emitAllocAnyInstr(sizeof(instrDescReloc), attr);
assert(id->idIsReloc());
id->idrRelocVal = addr;
+#if EMITTER_STATS
+ emitTotalIDescRelocCnt++;
+#endif // EMITTER_STATS
+
return id;
}
diff --git a/src/jit/emitarm.cpp b/src/jit/emitarm.cpp
index 8098c60a41..3d7089bb8a 100644
--- a/src/jit/emitarm.cpp
+++ b/src/jit/emitarm.cpp
@@ -3344,9 +3344,7 @@ void emitter::emitIns_R_R_R_I(instruction ins,
assert(sf != INS_FLAGS_DONT_CARE);
// 3-reg ops can't use the small instrdesc
- instrDescCns* id = emitAllocInstrCns(attr);
- id->idSetIsLargeCns();
- id->idcCnsVal = imm;
+ instrDesc* id = emitNewInstrCns(attr, imm);
id->idIns(ins);
id->idInsFmt(fmt);
diff --git a/src/jit/emitarm64.cpp b/src/jit/emitarm64.cpp
index 9f4208a4a5..d066edfd27 100644
--- a/src/jit/emitarm64.cpp
+++ b/src/jit/emitarm64.cpp
@@ -7020,7 +7020,7 @@ void emitter::emitIns_R_AI(instruction ins, emitAttr attr, regNumber ireg, ssize
// add reg, reg, imm
ins = INS_add;
fmt = IF_DI_2A;
- instrDesc* id = emitAllocInstr(attr);
+ instrDesc* id = emitNewInstr(attr);
assert(id->idIsReloc());
id->idIns(ins);
diff --git a/src/jit/emitxarch.cpp b/src/jit/emitxarch.cpp
index bbaeb81d0f..8bba1f688a 100644
--- a/src/jit/emitxarch.cpp
+++ b/src/jit/emitxarch.cpp
@@ -2407,33 +2407,15 @@ emitter::instrDesc* emitter::emitNewInstrAmdCns(emitAttr size, ssize_t dsp, int
{
if (dsp >= AM_DISP_MIN && dsp <= AM_DISP_MAX)
{
- if (cns >= ID_MIN_SMALL_CNS && cns <= ID_MAX_SMALL_CNS)
- {
- instrDesc* id = emitAllocInstr(size);
-
- id->idSmallCns(cns);
-
- id->idAddr()->iiaAddrMode.amDisp = dsp;
- assert(id->idAddr()->iiaAddrMode.amDisp == dsp); // make sure the value fit
-
- return id;
- }
- else
- {
- instrDescCns* id = emitAllocInstrCns(size);
-
- id->idSetIsLargeCns();
- id->idcCnsVal = cns;
-
- id->idAddr()->iiaAddrMode.amDisp = dsp;
- assert(id->idAddr()->iiaAddrMode.amDisp == dsp); // make sure the value fit
+ instrDesc* id = emitNewInstrCns(size, cns);
+ id->idAddr()->iiaAddrMode.amDisp = dsp;
+ assert(id->idAddr()->iiaAddrMode.amDisp == dsp); // make sure the value fit
- return id;
- }
+ return id;
}
else
{
- if (cns >= ID_MIN_SMALL_CNS && cns <= ID_MAX_SMALL_CNS)
+ if (instrDesc::fitsInSmallCns(cns))
{
instrDescAmd* id = emitAllocInstrAmd(size);
diff --git a/src/jit/flowgraph.cpp b/src/jit/flowgraph.cpp
index 4888ce27ec..3f830f6f4c 100644
--- a/src/jit/flowgraph.cpp
+++ b/src/jit/flowgraph.cpp
@@ -15283,11 +15283,11 @@ void Compiler::fgReorderBlocks()
// is more than twice the max weight of the bPrev to block edge.
//
// bPrev --> [BB04, weight 31]
- // | \
- // edgeToBlock -------------> O \
- // [min=8,max=10] V \
- // block --> [BB05, weight 10] \
- // \
+ // | \.
+ // edgeToBlock -------------> O \.
+ // [min=8,max=10] V \.
+ // block --> [BB05, weight 10] \.
+ // \.
// edgeToDest ----------------------------> O
// [min=21,max=23] |
// V
@@ -15331,10 +15331,10 @@ void Compiler::fgReorderBlocks()
// 2. Check that the weight of bPrev is at least three times more than block
//
// bPrev --> [BB04, weight 31]
- // | \
- // V \
- // block --> [BB05, weight 10] \
- // \
+ // | \.
+ // V \.
+ // block --> [BB05, weight 10] \.
+ // \.
// |
// V
// bDest ---------------> [BB08, weight 21]
@@ -21069,8 +21069,8 @@ void Compiler::fgDebugCheckBBlist(bool checkBBNum /* = false */, bool checkBBRef
// written to or address-exposed.
assert(compThisArgAddrExposedOK && !lvaTable[info.compThisArg].lvHasILStoreOp &&
(lvaArg0Var == info.compThisArg ||
- lvaArg0Var != info.compThisArg && (lvaTable[lvaArg0Var].lvAddrExposed ||
- lvaTable[lvaArg0Var].lvHasILStoreOp || copiedForGenericsCtxt)));
+ (lvaArg0Var != info.compThisArg && (lvaTable[lvaArg0Var].lvAddrExposed ||
+ lvaTable[lvaArg0Var].lvHasILStoreOp || copiedForGenericsCtxt))));
}
}
diff --git a/src/jit/gcencode.cpp b/src/jit/gcencode.cpp
index 4ec6f4cb8b..331796c33a 100644
--- a/src/jit/gcencode.cpp
+++ b/src/jit/gcencode.cpp
@@ -3979,7 +3979,7 @@ void GCInfo::gcInfoBlockHdrSave(GcInfoEncoder* gcInfoEncoder, unsigned methodSiz
{
// The predicate above is true only if there is an extra generic context parameter, not for
// the case where the generic context is provided by "this."
- assert(compiler->info.compTypeCtxtArg != BAD_VAR_NUM);
+ assert((SIZE_T)compiler->info.compTypeCtxtArg != BAD_VAR_NUM);
GENERIC_CONTEXTPARAM_TYPE ctxtParamType = GENERIC_CONTEXTPARAM_NONE;
switch (compiler->info.compMethodInfo->options & CORINFO_GENERICS_CTXT_MASK)
{
diff --git a/src/jit/gcinfo.cpp b/src/jit/gcinfo.cpp
index 14ca8a873f..3f980ba370 100644
--- a/src/jit/gcinfo.cpp
+++ b/src/jit/gcinfo.cpp
@@ -638,8 +638,6 @@ void GCInfo::gcRegPtrSetInit()
GCInfo::WriteBarrierForm GCInfo::gcWriteBarrierFormFromTargetAddress(GenTree* tgtAddr)
{
- GCInfo::WriteBarrierForm result = GCInfo::WBF_BarrierUnknown; // Default case, we have no information.
-
// If we store through an int to a GC_REF field, we'll assume that needs to use a checked barriers.
if (tgtAddr->TypeGet() == TYP_I_IMPL)
{
diff --git a/src/jit/gschecks.cpp b/src/jit/gschecks.cpp
index 8fe14b9d9d..e52817a269 100644
--- a/src/jit/gschecks.cpp
+++ b/src/jit/gschecks.cpp
@@ -105,7 +105,6 @@ Compiler::fgWalkResult Compiler::gsMarkPtrsAndAssignGroups(GenTree** pTree, fgWa
GenTree* tree = *pTree;
ShadowParamVarInfo* shadowVarInfo = pState->comp->gsShadowVarInfo;
assert(shadowVarInfo);
- bool fIsBlk = false;
unsigned lclNum;
assert(!pState->isAssignSrc || pState->lvAssignDef != (unsigned)-1);
diff --git a/src/jit/gtlist.h b/src/jit/gtlist.h
index dd23db486b..2421a8f033 100644
--- a/src/jit/gtlist.h
+++ b/src/jit/gtlist.h
@@ -20,12 +20,12 @@ GTNODE(NONE , char ,0,GTK_SPECIAL)
// Leaf nodes (i.e. these nodes have no sub-operands):
//-----------------------------------------------------------------------------
-GTNODE(LCL_VAR , GenTreeLclVar ,0,GTK_LEAF|GTK_LOCAL) // local variable
-GTNODE(LCL_FLD , GenTreeLclFld ,0,GTK_LEAF|GTK_LOCAL) // field in a non-primitive variable
+GTNODE(LCL_VAR , GenTreeLclVar ,0,(GTK_LEAF|GTK_LOCAL)) // local variable
+GTNODE(LCL_FLD , GenTreeLclFld ,0,(GTK_LEAF|GTK_LOCAL)) // field in a non-primitive variable
GTNODE(LCL_VAR_ADDR , GenTreeLclVar ,0,GTK_LEAF) // address of local variable
GTNODE(LCL_FLD_ADDR , GenTreeLclFld ,0,GTK_LEAF) // address of field in a non-primitive variable
-GTNODE(STORE_LCL_VAR , GenTreeLclVar ,0,GTK_UNOP|GTK_LOCAL|GTK_NOVALUE) // store to local variable
-GTNODE(STORE_LCL_FLD , GenTreeLclFld ,0,GTK_UNOP|GTK_LOCAL|GTK_NOVALUE) // store to field in a non-primitive variable
+GTNODE(STORE_LCL_VAR , GenTreeLclVar ,0,(GTK_UNOP|GTK_LOCAL|GTK_NOVALUE)) // store to local variable
+GTNODE(STORE_LCL_FLD , GenTreeLclFld ,0,(GTK_UNOP|GTK_LOCAL|GTK_NOVALUE)) // store to field in a non-primitive variable
GTNODE(CATCH_ARG , GenTree ,0,GTK_LEAF) // Exception object in a catch block
GTNODE(LABEL , GenTree ,0,GTK_LEAF) // Jump-target
GTNODE(FTN_ADDR , GenTreeFptrVal ,0,GTK_LEAF) // Address of a function
@@ -35,56 +35,56 @@ GTNODE(RET_EXPR , GenTreeRetExpr ,0,GTK_LEAF) // Place
// Constant nodes:
//-----------------------------------------------------------------------------
-GTNODE(CNS_INT , GenTreeIntCon ,0,GTK_LEAF|GTK_CONST)
-GTNODE(CNS_LNG , GenTreeLngCon ,0,GTK_LEAF|GTK_CONST)
-GTNODE(CNS_DBL , GenTreeDblCon ,0,GTK_LEAF|GTK_CONST)
-GTNODE(CNS_STR , GenTreeStrCon ,0,GTK_LEAF|GTK_CONST)
+GTNODE(CNS_INT , GenTreeIntCon ,0,(GTK_LEAF|GTK_CONST))
+GTNODE(CNS_LNG , GenTreeLngCon ,0,(GTK_LEAF|GTK_CONST))
+GTNODE(CNS_DBL , GenTreeDblCon ,0,(GTK_LEAF|GTK_CONST))
+GTNODE(CNS_STR , GenTreeStrCon ,0,(GTK_LEAF|GTK_CONST))
//-----------------------------------------------------------------------------
// Unary operators (1 operand):
//-----------------------------------------------------------------------------
GTNODE(NOT , GenTreeOp ,0,GTK_UNOP)
-GTNODE(NOP , GenTree ,0,GTK_UNOP|GTK_NOCONTAIN)
+GTNODE(NOP , GenTree ,0,(GTK_UNOP|GTK_NOCONTAIN))
GTNODE(NEG , GenTreeOp ,0,GTK_UNOP)
GTNODE(COPY , GenTreeCopyOrReload,0,GTK_UNOP) // Copies a variable from its current location to a register that satisfies
// code generation constraints. The child is the actual lclVar node.
GTNODE(RELOAD , GenTreeCopyOrReload,0,GTK_UNOP)
-GTNODE(ARR_LENGTH , GenTreeArrLen ,0,GTK_UNOP|GTK_EXOP) // array-length
-GTNODE(INTRINSIC , GenTreeIntrinsic ,0,GTK_BINOP|GTK_EXOP) // intrinsics
+GTNODE(ARR_LENGTH , GenTreeArrLen ,0,(GTK_UNOP|GTK_EXOP)) // array-length
+GTNODE(INTRINSIC , GenTreeIntrinsic ,0,(GTK_BINOP|GTK_EXOP)) // intrinsics
-GTNODE(LOCKADD , GenTreeOp ,0,GTK_BINOP|GTK_NOVALUE)
+GTNODE(LOCKADD , GenTreeOp ,0,(GTK_BINOP|GTK_NOVALUE))
GTNODE(XADD , GenTreeOp ,0,GTK_BINOP)
GTNODE(XCHG , GenTreeOp ,0,GTK_BINOP)
GTNODE(CMPXCHG , GenTreeCmpXchg ,0,GTK_SPECIAL)
-GTNODE(MEMORYBARRIER , GenTree ,0,GTK_LEAF|GTK_NOVALUE)
+GTNODE(MEMORYBARRIER , GenTree ,0,(GTK_LEAF|GTK_NOVALUE))
-GTNODE(CAST , GenTreeCast ,0,GTK_UNOP|GTK_EXOP) // conversion to another type
+GTNODE(CAST , GenTreeCast ,0,(GTK_UNOP|GTK_EXOP)) // conversion to another type
#if defined(_TARGET_ARM_)
GTNODE(BITCAST , GenTreeMultiRegOp ,0,GTK_UNOP) // reinterpretation of bits as another type
#else
GTNODE(BITCAST , GenTreeOp ,0,GTK_UNOP) // reinterpretation of bits as another type
#endif
-GTNODE(CKFINITE , GenTreeOp ,0,GTK_UNOP|GTK_NOCONTAIN) // Check for NaN
-GTNODE(LCLHEAP , GenTreeOp ,0,GTK_UNOP|GTK_NOCONTAIN) // alloca()
-GTNODE(JMP , GenTreeVal ,0,GTK_LEAF|GTK_NOVALUE) // Jump to another function
+GTNODE(CKFINITE , GenTreeOp ,0,(GTK_UNOP|GTK_NOCONTAIN)) // Check for NaN
+GTNODE(LCLHEAP , GenTreeOp ,0,(GTK_UNOP|GTK_NOCONTAIN)) // alloca()
+GTNODE(JMP , GenTreeVal ,0,(GTK_LEAF|GTK_NOVALUE)) // Jump to another function
GTNODE(ADDR , GenTreeOp ,0,GTK_UNOP) // address of
GTNODE(IND , GenTreeOp ,0,GTK_UNOP) // load indirection
-GTNODE(STOREIND , GenTreeStoreInd ,0,GTK_BINOP|GTK_NOVALUE) // store indirection
+GTNODE(STOREIND , GenTreeStoreInd ,0,(GTK_BINOP|GTK_NOVALUE)) // store indirection
// TODO-Cleanup: GT_ARR_BOUNDS_CHECK should be made a GTK_BINOP now that it has only two child nodes
-GTNODE(ARR_BOUNDS_CHECK , GenTreeBoundsChk ,0,GTK_SPECIAL|GTK_NOVALUE)// array bounds check
-GTNODE(OBJ , GenTreeObj ,0,GTK_UNOP|GTK_EXOP) // Object that MAY have gc pointers, and thus includes the relevant gc layout info.
-GTNODE(STORE_OBJ , GenTreeBlk ,0,GTK_BINOP|GTK_EXOP|GTK_NOVALUE) // Object that MAY have gc pointers, and thus includes the relevant gc layout info.
+GTNODE(ARR_BOUNDS_CHECK , GenTreeBoundsChk ,0,(GTK_SPECIAL|GTK_NOVALUE))// array bounds check
+GTNODE(OBJ , GenTreeObj ,0,(GTK_UNOP|GTK_EXOP)) // Object that MAY have gc pointers, and thus includes the relevant gc layout info.
+GTNODE(STORE_OBJ , GenTreeBlk ,0,(GTK_BINOP|GTK_EXOP|GTK_NOVALUE)) // Object that MAY have gc pointers, and thus includes the relevant gc layout info.
GTNODE(BLK , GenTreeBlk ,0,GTK_UNOP) // Block/object with no gc pointers, and with a known size (e.g. a struct with no gc fields)
-GTNODE(STORE_BLK , GenTreeBlk ,0,GTK_BINOP|GTK_NOVALUE) // Block/object with no gc pointers, and with a known size (e.g. a struct with no gc fields)
+GTNODE(STORE_BLK , GenTreeBlk ,0,(GTK_BINOP|GTK_NOVALUE)) // Block/object with no gc pointers, and with a known size (e.g. a struct with no gc fields)
GTNODE(DYN_BLK , GenTreeBlk ,0,GTK_SPECIAL) // Dynamically sized block object
-GTNODE(STORE_DYN_BLK , GenTreeBlk ,0,GTK_SPECIAL|GTK_NOVALUE)// Dynamically sized block object
-GTNODE(BOX , GenTreeBox ,0,GTK_UNOP|GTK_EXOP|GTK_NOTLIR)
+GTNODE(STORE_DYN_BLK , GenTreeBlk ,0,(GTK_SPECIAL|GTK_NOVALUE))// Dynamically sized block object
+GTNODE(BOX , GenTreeBox ,0,(GTK_UNOP|GTK_EXOP|GTK_NOTLIR))
#ifdef FEATURE_SIMD
-GTNODE(SIMD_CHK , GenTreeBoundsChk ,0,GTK_SPECIAL|GTK_NOVALUE)// Compare whether an index is less than the given SIMD vector length, and call CORINFO_HELP_RNGCHKFAIL if not.
+GTNODE(SIMD_CHK , GenTreeBoundsChk ,0,(GTK_SPECIAL|GTK_NOVALUE))// Compare whether an index is less than the given SIMD vector length, and call CORINFO_HELP_RNGCHKFAIL if not.
// TODO-CQ: In future may want to add a field that specifies different exceptions but we'll
// need VM assistance for that.
// TODO-CQ: It would actually be very nice to make this an unconditional throw, and expose the control flow that
@@ -92,14 +92,14 @@ GTNODE(SIMD_CHK , GenTreeBoundsChk ,0,GTK_SPECIAL|GTK_NOVALUE)// Compa
#endif // FEATURE_SIMD
#ifdef FEATURE_HW_INTRINSICS
-GTNODE(HW_INTRINSIC_CHK , GenTreeBoundsChk ,0,GTK_SPECIAL|GTK_NOVALUE)// Compare whether an imm8 argument is in the valid range, and throw ArgumentOutOfRangeException if not.
+GTNODE(HW_INTRINSIC_CHK , GenTreeBoundsChk ,0,(GTK_SPECIAL|GTK_NOVALUE))// Compare whether an imm8 argument is in the valid range, and throw ArgumentOutOfRangeException if not.
#endif
-GTNODE(ALLOCOBJ , GenTreeAllocObj ,0,GTK_UNOP|GTK_EXOP) // object allocator
+GTNODE(ALLOCOBJ , GenTreeAllocObj ,0,(GTK_UNOP|GTK_EXOP)) // object allocator
GTNODE(INIT_VAL , GenTreeOp ,0,GTK_UNOP) // Initialization value for an initBlk
-GTNODE(RUNTIMELOOKUP , GenTreeRuntimeLookup, 0,GTK_UNOP|GTK_EXOP) // Runtime handle lookup
+GTNODE(RUNTIMELOOKUP , GenTreeRuntimeLookup, 0,(GTK_UNOP|GTK_EXOP)) // Runtime handle lookup
GTNODE(BSWAP , GenTreeOp ,0,GTK_UNOP) // Byte swap (32-bit or 64-bit)
GTNODE(BSWAP16 , GenTreeOp ,0,GTK_UNOP) // Byte swap (16-bit)
@@ -117,9 +117,9 @@ GTNODE(MOD , GenTreeOp ,0,GTK_BINOP)
GTNODE(UDIV , GenTreeOp ,0,GTK_BINOP)
GTNODE(UMOD , GenTreeOp ,0,GTK_BINOP)
-GTNODE(OR , GenTreeOp ,1,GTK_BINOP|GTK_LOGOP)
-GTNODE(XOR , GenTreeOp ,1,GTK_BINOP|GTK_LOGOP)
-GTNODE(AND , GenTreeOp ,1,GTK_BINOP|GTK_LOGOP)
+GTNODE(OR , GenTreeOp ,1,(GTK_BINOP|GTK_LOGOP))
+GTNODE(XOR , GenTreeOp ,1,(GTK_BINOP|GTK_LOGOP))
+GTNODE(AND , GenTreeOp ,1,(GTK_BINOP|GTK_LOGOP))
GTNODE(LSH , GenTreeOp ,0,GTK_BINOP)
GTNODE(RSH , GenTreeOp ,0,GTK_BINOP)
@@ -131,13 +131,13 @@ GTNODE(MULHI , GenTreeOp ,1,GTK_BINOP) // returns high bits
// the div into a MULHI + some adjustments. In codegen, we only use the
// results of the high register, and we drop the low results.
-GTNODE(ASG , GenTreeOp ,0,GTK_BINOP|GTK_NOTLIR)
-GTNODE(EQ , GenTreeOp ,0,GTK_BINOP|GTK_RELOP)
-GTNODE(NE , GenTreeOp ,0,GTK_BINOP|GTK_RELOP)
-GTNODE(LT , GenTreeOp ,0,GTK_BINOP|GTK_RELOP)
-GTNODE(LE , GenTreeOp ,0,GTK_BINOP|GTK_RELOP)
-GTNODE(GE , GenTreeOp ,0,GTK_BINOP|GTK_RELOP)
-GTNODE(GT , GenTreeOp ,0,GTK_BINOP|GTK_RELOP)
+GTNODE(ASG , GenTreeOp ,0,(GTK_BINOP|GTK_NOTLIR))
+GTNODE(EQ , GenTreeOp ,0,(GTK_BINOP|GTK_RELOP))
+GTNODE(NE , GenTreeOp ,0,(GTK_BINOP|GTK_RELOP))
+GTNODE(LT , GenTreeOp ,0,(GTK_BINOP|GTK_RELOP))
+GTNODE(LE , GenTreeOp ,0,(GTK_BINOP|GTK_RELOP))
+GTNODE(GE , GenTreeOp ,0,(GTK_BINOP|GTK_RELOP))
+GTNODE(GT , GenTreeOp ,0,(GTK_BINOP|GTK_RELOP))
// These are similar to GT_EQ/GT_NE but they generate "test" instead of "cmp" instructions.
// Currently these are generated during lowering for code like ((x & y) eq|ne 0) only on
@@ -146,21 +146,21 @@ GTNODE(GT , GenTreeOp ,0,GTK_BINOP|GTK_RELOP)
// codegen which emits a "test reg, reg" instruction, that would be more difficult to do
// during lowering because the source operand is used twice so it has to be a lclvar.
// Because of this there is no need to also add GT_TEST_LT/LE/GE/GT opers.
-GTNODE(TEST_EQ , GenTreeOp ,0,GTK_BINOP|GTK_RELOP)
-GTNODE(TEST_NE , GenTreeOp ,0,GTK_BINOP|GTK_RELOP)
+GTNODE(TEST_EQ , GenTreeOp ,0,(GTK_BINOP|GTK_RELOP))
+GTNODE(TEST_NE , GenTreeOp ,0,(GTK_BINOP|GTK_RELOP))
-GTNODE(COMMA , GenTreeOp ,0,GTK_BINOP|GTK_NOTLIR)
+GTNODE(COMMA , GenTreeOp ,0,(GTK_BINOP|GTK_NOTLIR))
-GTNODE(QMARK , GenTreeQmark ,0,GTK_BINOP|GTK_EXOP|GTK_NOTLIR)
-GTNODE(COLON , GenTreeColon ,0,GTK_BINOP|GTK_NOTLIR)
+GTNODE(QMARK , GenTreeQmark ,0,(GTK_BINOP|GTK_EXOP|GTK_NOTLIR))
+GTNODE(COLON , GenTreeColon ,0,(GTK_BINOP|GTK_NOTLIR))
-GTNODE(INDEX , GenTreeIndex ,0,GTK_BINOP|GTK_EXOP|GTK_NOTLIR) // SZ-array-element
-GTNODE(INDEX_ADDR , GenTreeIndex ,0,GTK_BINOP|GTK_EXOP) // addr of SZ-array-element; used when
+GTNODE(INDEX , GenTreeIndex ,0,(GTK_BINOP|GTK_EXOP|GTK_NOTLIR)) // SZ-array-element
+GTNODE(INDEX_ADDR , GenTreeIndex ,0,(GTK_BINOP|GTK_EXOP)) // addr of SZ-array-element; used when
// aiming to minimize compile times.
GTNODE(MKREFANY , GenTreeOp ,0,GTK_BINOP)
-GTNODE(LEA , GenTreeAddrMode ,0,GTK_BINOP|GTK_EXOP)
+GTNODE(LEA , GenTreeAddrMode ,0,(GTK_BINOP|GTK_EXOP))
#if !defined(_TARGET_64BIT_)
// A GT_LONG node simply represents the long value produced by the concatenation
@@ -200,35 +200,35 @@ GTNODE(RSH_LO , GenTreeOp ,0,GTK_BINOP)
#endif // !defined(_TARGET_64BIT_)
#ifdef FEATURE_SIMD
-GTNODE(SIMD , GenTreeSIMD ,0,GTK_BINOP|GTK_EXOP) // SIMD functions/operators/intrinsics
+GTNODE(SIMD , GenTreeSIMD ,0,(GTK_BINOP|GTK_EXOP)) // SIMD functions/operators/intrinsics
#endif // FEATURE_SIMD
#ifdef FEATURE_HW_INTRINSICS
-GTNODE(HWIntrinsic , GenTreeHWIntrinsic ,0,GTK_BINOP|GTK_EXOP) // hardware intrinsics
+GTNODE(HWIntrinsic , GenTreeHWIntrinsic ,0,(GTK_BINOP|GTK_EXOP)) // hardware intrinsics
#endif // FEATURE_HW_INTRINSICS
//-----------------------------------------------------------------------------
// LIR specific compare and conditional branch/set nodes:
//-----------------------------------------------------------------------------
-GTNODE(CMP , GenTreeOp ,0,GTK_BINOP|GTK_NOVALUE) // Sets the condition flags according to the compare result.
+GTNODE(CMP , GenTreeOp ,0,(GTK_BINOP|GTK_NOVALUE)) // Sets the condition flags according to the compare result.
// N.B. Not a relop, it does not produce a value and it cannot be reversed.
-GTNODE(JCMP , GenTreeOp ,0,GTK_BINOP|GTK_NOVALUE) // Makes a comparison and jump if the condition specified. Does not set flags
-GTNODE(JCC , GenTreeCC ,0,GTK_LEAF|GTK_NOVALUE) // Checks the condition flags and branch if the condition specified
+GTNODE(JCMP , GenTreeOp ,0,(GTK_BINOP|GTK_NOVALUE)) // Makes a comparison and jump if the condition specified. Does not set flags
+GTNODE(JCC , GenTreeCC ,0,(GTK_LEAF|GTK_NOVALUE)) // Checks the condition flags and branch if the condition specified
// by GenTreeCC::gtCondition is true.
GTNODE(SETCC , GenTreeCC ,0,GTK_LEAF) // Checks the condition flags and produces 1 if the condition specified
// by GenTreeCC::gtCondition is true and 0 otherwise.
#ifdef _TARGET_XARCH_
-GTNODE(BT , GenTreeOp ,0,GTK_BINOP|GTK_NOVALUE) // The XARCH BT instruction. Like CMP, this sets the condition flags (CF
+GTNODE(BT , GenTreeOp ,0,(GTK_BINOP|GTK_NOVALUE)) // The XARCH BT instruction. Like CMP, this sets the condition flags (CF
// to be precise) and does not produce a value.
#endif
//-----------------------------------------------------------------------------
// Other nodes that look like unary/binary operators:
//-----------------------------------------------------------------------------
-GTNODE(JTRUE , GenTreeOp ,0,GTK_UNOP|GTK_NOVALUE)
+GTNODE(JTRUE , GenTreeOp ,0,(GTK_UNOP|GTK_NOVALUE))
-GTNODE(LIST , GenTreeArgList ,0,GTK_BINOP|GTK_NOVALUE)
+GTNODE(LIST , GenTreeArgList ,0,(GTK_BINOP|GTK_NOVALUE))
GTNODE(FIELD_LIST , GenTreeFieldList ,0,GTK_BINOP) // List of fields of a struct, when passed as an argument
//-----------------------------------------------------------------------------
@@ -237,31 +237,31 @@ GTNODE(FIELD_LIST , GenTreeFieldList ,0,GTK_BINOP) // List of fields of
GTNODE(FIELD , GenTreeField ,0,GTK_SPECIAL) // Member-field
GTNODE(ARR_ELEM , GenTreeArrElem ,0,GTK_SPECIAL) // Multi-dimensional array-element address
-GTNODE(ARR_INDEX , GenTreeArrIndex ,0,GTK_BINOP|GTK_EXOP) // Effective, bounds-checked index for one dimension of a multi-dimensional array element
+GTNODE(ARR_INDEX , GenTreeArrIndex ,0,(GTK_BINOP|GTK_EXOP)) // Effective, bounds-checked index for one dimension of a multi-dimensional array element
GTNODE(ARR_OFFSET , GenTreeArrOffs ,0,GTK_SPECIAL) // Flattened offset of multi-dimensional array element
-GTNODE(CALL , GenTreeCall ,0,GTK_SPECIAL|GTK_NOCONTAIN)
+GTNODE(CALL , GenTreeCall ,0,(GTK_SPECIAL|GTK_NOCONTAIN))
//-----------------------------------------------------------------------------
// Statement operator nodes:
//-----------------------------------------------------------------------------
-GTNODE(BEG_STMTS , GenTree ,0,GTK_SPECIAL|GTK_NOVALUE)// used only temporarily in importer by impBegin/EndTreeList()
-GTNODE(STMT , GenTreeStmt ,0,GTK_SPECIAL|GTK_NOVALUE)// top-level list nodes in bbTreeList
+GTNODE(BEG_STMTS , GenTree ,0,(GTK_SPECIAL|GTK_NOVALUE))// used only temporarily in importer by impBegin/EndTreeList()
+GTNODE(STMT , GenTreeStmt ,0,(GTK_SPECIAL|GTK_NOVALUE))// top-level list nodes in bbTreeList
-GTNODE(RETURN , GenTreeOp ,0,GTK_UNOP|GTK_NOVALUE) // return from current function
-GTNODE(SWITCH , GenTreeOp ,0,GTK_UNOP|GTK_NOVALUE) // switch
+GTNODE(RETURN , GenTreeOp ,0,(GTK_UNOP|GTK_NOVALUE)) // return from current function
+GTNODE(SWITCH , GenTreeOp ,0,(GTK_UNOP|GTK_NOVALUE)) // switch
-GTNODE(NO_OP , GenTree ,0,GTK_LEAF|GTK_NOVALUE) // nop!
+GTNODE(NO_OP , GenTree ,0,(GTK_LEAF|GTK_NOVALUE)) // nop!
-GTNODE(START_NONGC , GenTree ,0,GTK_LEAF|GTK_NOVALUE) // starts a new instruction group that will be non-gc interruptible
+GTNODE(START_NONGC , GenTree ,0,(GTK_LEAF|GTK_NOVALUE)) // starts a new instruction group that will be non-gc interruptible
-GTNODE(START_PREEMPTGC , GenTree ,0,GTK_LEAF|GTK_NOVALUE) // starts a new instruction group where preemptive GC is enabled
+GTNODE(START_PREEMPTGC , GenTree ,0,(GTK_LEAF|GTK_NOVALUE)) // starts a new instruction group where preemptive GC is enabled
-GTNODE(PROF_HOOK , GenTree ,0,GTK_LEAF|GTK_NOVALUE) // profiler Enter/Leave/TailCall hook
+GTNODE(PROF_HOOK , GenTree ,0,(GTK_LEAF|GTK_NOVALUE)) // profiler Enter/Leave/TailCall hook
-GTNODE(RETFILT , GenTreeOp ,0,GTK_UNOP|GTK_NOVALUE) // end filter with TYP_I_IMPL return value
+GTNODE(RETFILT , GenTreeOp ,0,(GTK_UNOP|GTK_NOVALUE)) // end filter with TYP_I_IMPL return value
#if !FEATURE_EH_FUNCLETS
-GTNODE(END_LFIN , GenTreeVal ,0,GTK_LEAF|GTK_NOVALUE) // end locally-invoked finally
+GTNODE(END_LFIN , GenTreeVal ,0,(GTK_LEAF|GTK_NOVALUE)) // end locally-invoked finally
#endif // !FEATURE_EH_FUNCLETS
//-----------------------------------------------------------------------------
@@ -269,14 +269,14 @@ GTNODE(END_LFIN , GenTreeVal ,0,GTK_LEAF|GTK_NOVALUE) // end l
//-----------------------------------------------------------------------------
GTNODE(PHI , GenTreeOp ,0,GTK_UNOP) // phi node for ssa.
-GTNODE(PHI_ARG , GenTreePhiArg ,0,GTK_LEAF|GTK_LOCAL) // phi(phiarg, phiarg, phiarg)
+GTNODE(PHI_ARG , GenTreePhiArg ,0,(GTK_LEAF|GTK_LOCAL)) // phi(phiarg, phiarg, phiarg)
//-----------------------------------------------------------------------------
// Nodes used by Lower to generate a closer CPU representation of other nodes
//-----------------------------------------------------------------------------
-GTNODE(JMPTABLE , GenTreeJumpTable ,0, GTK_LEAF|GTK_NOCONTAIN) // Generates the jump table for switches
-GTNODE(SWITCH_TABLE , GenTreeOp ,0, GTK_BINOP|GTK_NOVALUE) // Jump Table based switch construct
+GTNODE(JMPTABLE , GenTreeJumpTable ,0, (GTK_LEAF|GTK_NOCONTAIN)) // Generates the jump table for switches
+GTNODE(SWITCH_TABLE , GenTreeOp ,0, (GTK_BINOP|GTK_NOVALUE)) // Jump Table based switch construct
//-----------------------------------------------------------------------------
// Nodes used only within the code generator:
diff --git a/src/jit/hashbv.cpp b/src/jit/hashbv.cpp
index 6aee93ee33..5539baa2ca 100644
--- a/src/jit/hashbv.cpp
+++ b/src/jit/hashbv.cpp
@@ -426,8 +426,6 @@ hashBv*& hashBv::hbvFreeList(hashBvGlobalData* data)
void hashBv::hbvFree()
{
- Compiler* comp = this->compiler;
-
int hts = hashtable_size();
for (int i = 0; i < hts; i++)
{
@@ -571,8 +569,6 @@ void hashBv::Resize(int newSize)
}
else if (oldSize > newSize)
{
- int shrinkFactor = oldSize / newSize;
-
// shrink multiple lists into one list
// more efficient ways to do this but...
// if the lists are long, you shouldn't be shrinking.
@@ -922,8 +918,6 @@ int hashBv::countBits()
bool hashBv::anySet()
{
- int result = 0;
-
int hts = this->hashtable_size();
for (int hashNum = 0; hashNum < hts; hashNum++)
{
@@ -1299,7 +1293,6 @@ bool hashBv::MultiTraverseLHSBigger(hashBv* other)
hashBvNode* o = other->nodeArr[h];
while (o)
{
- hashBvNode* next = o->next;
// figure out what dst list this goes to
int hash = getHashForIndex(o->baseIndex, hts);
int dstIndex = (hash - h) >> other->log2_hashSize;
@@ -1359,7 +1352,6 @@ bool hashBv::MultiTraverseLHSBigger(hashBv* other)
template <typename Action>
bool hashBv::MultiTraverseRHSBigger(hashBv* other)
{
- int hts = this->hashtable_size();
int ots = other->hashtable_size();
bool result = Action::DefaultResult();
@@ -1459,8 +1451,6 @@ bool hashBv::MultiTraverseEqual(hashBv* other)
for (int hashNum = 0; hashNum < hts; hashNum++)
{
- int destination = getHashForIndex(BITS_PER_NODE * hashNum, this->hashtable_size());
-
hashBvNode** pa = &this->nodeArr[hashNum];
hashBvNode** pb = &other->nodeArr[hashNum];
hashBvNode* b = *pb;
@@ -1520,8 +1510,6 @@ bool hashBv::MultiTraverseEqual(hashBv* other)
template <class Action>
bool hashBv::MultiTraverse(hashBv* other)
{
- bool result = false;
-
assert(this->numNodes == this->getNodeCount());
Action::PreAction(this, other);
@@ -1646,8 +1634,6 @@ void hashBv::copyFrom(hashBv* other, Compiler* comp)
while (otherNode)
{
// printf("otherNode is True...\n");
- hashBvNode* next = *splicePoint;
-
this->numNodes++;
if (freeList)
diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp
index 08830deb9a..ab99a5c294 100644
--- a/src/jit/importer.cpp
+++ b/src/jit/importer.cpp
@@ -108,10 +108,10 @@ void Compiler::impPushOnStack(GenTree* tree, typeInfo ti)
// attempts to do that have proved too difficult. Instead, we'll assume that in checks like this,
// when there's a mismatch, it's because of this reason -- the typeInfo::AreEquivalentModuloNativeInt
// method used in the last disjunct allows exactly this mismatch.
- assert(ti.IsDead() || ti.IsByRef() && (tree->TypeGet() == TYP_I_IMPL || tree->TypeGet() == TYP_BYREF) ||
- ti.IsUnboxedGenericTypeVar() && tree->TypeGet() == TYP_REF ||
- ti.IsObjRef() && tree->TypeGet() == TYP_REF || ti.IsMethod() && tree->TypeGet() == TYP_I_IMPL ||
- ti.IsType(TI_STRUCT) && tree->TypeGet() != TYP_REF ||
+ assert(ti.IsDead() || (ti.IsByRef() && (tree->TypeGet() == TYP_I_IMPL) || tree->TypeGet() == TYP_BYREF) ||
+ (ti.IsUnboxedGenericTypeVar() && tree->TypeGet() == TYP_REF) ||
+ (ti.IsObjRef() && tree->TypeGet() == TYP_REF) || (ti.IsMethod() && tree->TypeGet() == TYP_I_IMPL) ||
+ (ti.IsType(TI_STRUCT) && tree->TypeGet() != TYP_REF) ||
typeInfo::AreEquivalentModuloNativeInt(NormaliseForStack(ti),
NormaliseForStack(typeInfo(tree->TypeGet()))));
@@ -10139,10 +10139,10 @@ var_types Compiler::impGetByRefResultType(genTreeOps oper, bool fUnsigned, GenTr
// to have a tree like this:
//
// -
- // / \
- // / \
- // / \
- // / \
+ // / \.
+ // / \.
+ // / \.
+ // / \.
// const(h) int addr byref
//
// <BUGNUM> VSW 318822 </BUGNUM>
@@ -10450,7 +10450,7 @@ GenTree* Compiler::impCastClassOrIsInstToTree(GenTree* op1,
// expand the methodtable match:
//
// condMT ==> GT_NE
- // / \
+ // / \.
// GT_IND op2 (typically CNS_INT)
// |
// op1Copy
@@ -10479,7 +10479,7 @@ GenTree* Compiler::impCastClassOrIsInstToTree(GenTree* op1,
// expand the null check:
//
// condNull ==> GT_EQ
- // / \
+ // / \.
// op1Copy CNS_INT
// null
//
@@ -10512,9 +10512,9 @@ GenTree* Compiler::impCastClassOrIsInstToTree(GenTree* op1,
// Generate first QMARK - COLON tree
//
// qmarkMT ==> GT_QMARK
- // / \
+ // / \.
// condMT GT_COLON
- // / \
+ // / \.
// condFalse condTrue
//
temp = new (this, GT_COLON) GenTreeColon(TYP_REF, condTrue, condFalse);
@@ -10525,9 +10525,9 @@ GenTree* Compiler::impCastClassOrIsInstToTree(GenTree* op1,
// Generate second QMARK - COLON tree
//
// qmarkNull ==> GT_QMARK
- // / \
+ // / \.
// condNull GT_COLON
- // / \
+ // / \.
// qmarkMT op1Copy
//
temp = new (this, GT_COLON) GenTreeColon(TYP_REF, gtClone(op1), qmarkMT);
@@ -18169,8 +18169,8 @@ BOOL Compiler::impIsAddressInLocal(GenTree* tree, GenTree** lclVarTreeOut)
void Compiler::impMakeDiscretionaryInlineObservations(InlineInfo* pInlineInfo, InlineResult* inlineResult)
{
- assert(pInlineInfo != nullptr && compIsForInlining() || // Perform the actual inlining.
- pInlineInfo == nullptr && !compIsForInlining() // Calculate the static inlining hint for ngen.
+ assert((pInlineInfo != nullptr && compIsForInlining()) || // Perform the actual inlining.
+ (pInlineInfo == nullptr && !compIsForInlining()) // Calculate the static inlining hint for ngen.
);
// If we're really inlining, we should just have one result in play.
diff --git a/src/jit/jiteh.cpp b/src/jit/jiteh.cpp
index bf476980fa..5179dd23dc 100644
--- a/src/jit/jiteh.cpp
+++ b/src/jit/jiteh.cpp
@@ -907,8 +907,6 @@ void Compiler::ehGetCallFinallyBlockRange(unsigned finallyIndex, BasicBlock** be
assert(begBlk != nullptr);
assert(endBlk != nullptr);
- EHblkDsc* ehDsc = ehGetDsc(finallyIndex);
-
#if FEATURE_EH_CALLFINALLY_THUNKS
bool inTryRegion;
unsigned callFinallyRegionIndex = ehGetCallFinallyRegionIndex(finallyIndex, &inTryRegion);
@@ -934,8 +932,9 @@ void Compiler::ehGetCallFinallyBlockRange(unsigned finallyIndex, BasicBlock** be
}
}
#else // !FEATURE_EH_CALLFINALLY_THUNKS
- *begBlk = ehDsc->ebdTryBeg;
- *endBlk = ehDsc->ebdTryLast->bbNext;
+ EHblkDsc* ehDsc = ehGetDsc(finallyIndex);
+ *begBlk = ehDsc->ebdTryBeg;
+ *endBlk = ehDsc->ebdTryLast->bbNext;
#endif // !FEATURE_EH_CALLFINALLY_THUNKS
}
diff --git a/src/jit/jitstd/hashtable.h b/src/jit/jitstd/hashtable.h
index 05b033a746..27b47107aa 100644
--- a/src/jit/jitstd/hashtable.h
+++ b/src/jit/jitstd/hashtable.h
@@ -777,6 +777,7 @@ typename hashtable<Key, Value, Hash, Pred, Alloc, KeyOf>::size_type
hashtable<Key, Value, Hash, Pred, Alloc, KeyOf>::bucket_size(size_type size) const
{
rehash(size);
+ return bucket_count();
}
template <typename Key, typename Value, typename Hash, typename Pred, typename Alloc, typename KeyOf>
diff --git a/src/jit/lclvars.cpp b/src/jit/lclvars.cpp
index a251534bd1..722e18f452 100644
--- a/src/jit/lclvars.cpp
+++ b/src/jit/lclvars.cpp
@@ -550,7 +550,10 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo)
const unsigned argSigLen = info.compMethodInfo->args.numArgs;
+#ifdef _TARGET_ARM_
regMaskTP doubleAlignMask = RBM_NONE;
+#endif // _TARGET_ARM_
+
for (unsigned i = 0; i < argSigLen;
i++, varDscInfo->varNum++, varDscInfo->varDsc++, argLst = info.compCompHnd->getArgNext(argLst))
{
@@ -569,8 +572,12 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo)
}
// For ARM, ARM64, and AMD64 varargs, all arguments go in integer registers
- var_types argType = mangleVarArgsType(varDsc->TypeGet());
+ var_types argType = mangleVarArgsType(varDsc->TypeGet());
+
+#ifdef _TARGET_ARM_
var_types origArgType = argType;
+#endif // TARGET_ARM
+
// ARM softfp calling convention should affect only the floating point arguments.
// Otherwise there appear too many surplus pre-spills and other memory operations
// with the associated locations .
@@ -1351,7 +1358,7 @@ unsigned Compiler::compMapILvarNum(unsigned ILvarNum)
* Returns UNKNOWN_ILNUM if it can't be mapped.
*/
-unsigned Compiler::compMap2ILvarNum(unsigned varNum)
+unsigned Compiler::compMap2ILvarNum(unsigned varNum) const
{
if (compIsForInlining())
{
@@ -1992,8 +1999,6 @@ bool Compiler::StructPromotionHelper::TryPromoteStructField(lvaStructFieldInfo&
return false;
}
- COMP_HANDLE compHandl = compiler->info.compCompHnd;
-
// Do not promote if the single field is not aligned at its natural boundary within
// the struct field.
CORINFO_FIELD_HANDLE innerFieldHndl = compHandle->getFieldInClass(fieldInfo.fldTypeHnd, 0);
@@ -2451,7 +2456,6 @@ void Compiler::lvaSetStruct(unsigned varNum, CORINFO_CLASS_HANDLE typeHnd, bool
}
// Set the type and associated info if we haven't already set it.
- var_types structType = varDsc->lvType;
if (varDsc->lvType == TYP_UNDEF)
{
varDsc->lvType = TYP_STRUCT;
@@ -7290,7 +7294,7 @@ int Compiler::lvaGetCallerSPRelativeOffset(unsigned varNum)
return lvaToCallerSPRelativeOffset(varDsc->lvStkOffs, varDsc->lvFramePointerBased);
}
-int Compiler::lvaToCallerSPRelativeOffset(int offset, bool isFpBased)
+int Compiler::lvaToCallerSPRelativeOffset(int offset, bool isFpBased) const
{
assert(lvaDoneFrameLayout == FINAL_FRAME_LAYOUT);
diff --git a/src/jit/liveness.cpp b/src/jit/liveness.cpp
index c9d5452427..2fb225ebab 100644
--- a/src/jit/liveness.cpp
+++ b/src/jit/liveness.cpp
@@ -955,7 +955,6 @@ void Compiler::fgExtendDbgLifetimes()
VarSetOps::DiffD(this, initVars, block->bbLiveIn);
/* Add statements initializing the vars, if there are any to initialize */
- unsigned blockWeight = block->getBBWeight(this);
VarSetOps::Iter iter(this, initVars);
unsigned varIndex = 0;
@@ -1001,6 +1000,7 @@ void Compiler::fgExtendDbgLifetimes()
initRange.InsertBefore(nullptr, zero, store);
#if !defined(_TARGET_64BIT_)
+ unsigned blockWeight = block->getBBWeight(this);
DecomposeLongs::DecomposeRange(this, blockWeight, initRange);
#endif // !defined(_TARGET_64BIT_)
m_pLowering->LowerRange(block, initRange);
diff --git a/src/jit/loopcloning.h b/src/jit/loopcloning.h
index cd9aa9f946..d0ec6b6b98 100644
--- a/src/jit/loopcloning.h
+++ b/src/jit/loopcloning.h
@@ -113,7 +113,7 @@ struct ArrIndex
void Print(unsigned dim = -1)
{
printf("V%02d", arrLcl);
- for (unsigned i = 0; i < ((dim == -1) ? rank : dim); ++i)
+ for (unsigned i = 0; i < ((dim == (unsigned)-1) ? rank : dim); ++i)
{
printf("[V%02d]", indLcls.GetRef(i));
}
diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp
index 862b77a304..fb6b3f4417 100644
--- a/src/jit/morph.cpp
+++ b/src/jit/morph.cpp
@@ -4322,7 +4322,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
// call fgMorphMultiregStructArg on each of them.
//
// Arguments:
-// call : a GenTreeCall node that has one or more TYP_STRUCT arguments\
+// call : a GenTreeCall node that has one or more TYP_STRUCT arguments\.
//
// Notes:
// We only call fgMorphMultiregStructArg for struct arguments that are not passed as simple types.
@@ -6447,7 +6447,7 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac)
// Build this tree: IND(*) #
// |
// ADD(I_IMPL)
- // / \
+ // / \.
// / CNS(fldOffset)
// /
// /
@@ -6455,9 +6455,9 @@ GenTree* Compiler::fgMorphField(GenTree* tree, MorphAddrContext* mac)
// IND(I_IMPL) == [Base of this DLL's TLS]
// |
// ADD(I_IMPL)
- // / \
+ // / \.
// / CNS(IdValue*4) or MUL
- // / / \
+ // / / \.
// IND(I_IMPL) / CNS(4)
// | /
// CNS(TLS_HDL,0x2C) IND
@@ -9060,7 +9060,7 @@ GenTree* Compiler::fgMorphOneAsgBlockOp(GenTree* tree)
// See if we can do a simple transformation:
//
// GT_ASG <TYP_size>
- // / \
+ // / \.
// GT_IND GT_IND or CNS_INT
// | |
// [dest] [src]
@@ -12420,8 +12420,8 @@ DONE_MORPHING_CHILDREN:
// Here we look for the following tree
//
// EQ/NE
- // / \
- // op1 CNS 0/1
+ // / \.
+ // op1 CNS 0/1
//
ival2 = INT_MAX; // The value of INT_MAX for ival2 just means that the constant value is not 0 or 1
@@ -12445,12 +12445,12 @@ DONE_MORPHING_CHILDREN:
// Here we look for the following transformation
//
// EQ/NE Possible REVERSE(RELOP)
- // / \ / \
- // COMMA CNS 0/1 -> COMMA relop_op2
- // / \ / \
- // x RELOP x relop_op1
- // / \
- // relop_op1 relop_op2
+ // / \ / \.
+ // COMMA CNS 0/1 -> COMMA relop_op2
+ // / \ / \.
+ // x RELOP x relop_op1
+ // / \.
+ // relop_op1 relop_op2
//
//
//
@@ -12488,14 +12488,14 @@ DONE_MORPHING_CHILDREN:
// and when the LCL_VAR is a temp we can fold the tree:
//
// EQ/NE EQ/NE
- // / \ / \
- // COMMA CNS 0/1 -> RELOP CNS 0/1
- // / \ / \
- // ASG LCL_VAR
- // / \
- // LCL_VAR RELOP
- // / \
- //
+ // / \ / \.
+ // COMMA CNS 0/1 -> RELOP CNS 0/1
+ // / \ / \.
+ // ASG LCL_VAR
+ // / \.
+ // LCL_VAR RELOP
+ // / \.
+ //
GenTree* asg = op1->gtOp.gtOp1;
GenTree* lcl = op1->gtOp.gtOp2;
@@ -12562,9 +12562,9 @@ DONE_MORPHING_CHILDREN:
// Here we look for the following tree
//
// EQ/NE -> RELOP/!RELOP
- // / \ / \
+ // / \ / \.
// RELOP CNS 0/1
- // / \
+ // / \.
//
// Note that we will remove/destroy the EQ/NE node and move
// the RELOP up into it's location.
@@ -12594,12 +12594,12 @@ DONE_MORPHING_CHILDREN:
// Here we look for the following transformation:
//
// EQ/NE EQ/NE
- // / \ / \
- // AND CNS 0/1 -> AND CNS 0
- // / \ / \
- // RSZ/RSH CNS 1 x CNS (1 << y)
- // / \
- // x CNS_INT +y
+ // / \ / \.
+ // AND CNS 0/1 -> AND CNS 0
+ // / \ / \.
+ // RSZ/RSH CNS 1 x CNS (1 << y)
+ // / \.
+ // x CNS_INT +y
if (op1->gtOper == GT_AND)
{
@@ -14020,12 +14020,12 @@ GenTree* Compiler::fgMorphSmpOpOptional(GenTreeOp* tree)
{
// This takes
// + (tree)
- // / \
- // / \
- // / \
+ // / \.
+ // / \.
+ // / \.
// + (op1) op2
- // / \
- // \
+ // / \.
+ // \.
// ad2
//
// And it swaps ad2 and op2. If (op2) is varTypeIsGC, then this implies that (tree) is
@@ -14360,13 +14360,13 @@ GenTree* Compiler::fgRecognizeAndMorphBitwiseRotation(GenTree* tree)
// Check for a rotation pattern, e.g.,
//
// OR ROL
- // / \ / \
+ // / \ / \.
// LSH RSZ -> x y
- // / \ / \
+ // / \ / \.
// x AND x AND
- // / \ / \
+ // / \ / \.
// y 31 ADD 31
- // / \
+ // / \.
// NEG 32
// |
// y
diff --git a/src/jit/optimizer.cpp b/src/jit/optimizer.cpp
index 795625aaf2..0be4456d8d 100644
--- a/src/jit/optimizer.cpp
+++ b/src/jit/optimizer.cpp
@@ -7915,8 +7915,6 @@ void Compiler::optRemoveRangeCheck(GenTree* tree, GenTree* stmt)
noway_assert(bndsChkTree->OperIsBoundsCheck());
- GenTreeBoundsChk* bndsChk = tree->gtOp.gtOp1->AsBoundsChk();
-
#ifdef DEBUG
if (verbose)
{
diff --git a/src/jit/regset.h b/src/jit/regset.h
index 469178dce4..3e866be169 100644
--- a/src/jit/regset.h
+++ b/src/jit/regset.h
@@ -44,7 +44,7 @@ public:
RegSet(Compiler* compiler, GCInfo& gcInfo);
#ifdef _TARGET_ARM_
- regMaskTP rsMaskPreSpillRegs(bool includeAlignment)
+ regMaskTP rsMaskPreSpillRegs(bool includeAlignment) const
{
return includeAlignment ? (rsMaskPreSpillRegArg | rsMaskPreSpillAlign) : rsMaskPreSpillRegArg;
}
diff --git a/src/jit/scopeinfo.cpp b/src/jit/scopeinfo.cpp
index 24c6701868..9e8d6b911c 100644
--- a/src/jit/scopeinfo.cpp
+++ b/src/jit/scopeinfo.cpp
@@ -58,7 +58,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#include "emit.h"
#include "codegen.h"
-bool CodeGenInterface::siVarLoc::vlIsInReg(regNumber reg)
+bool CodeGenInterface::siVarLoc::vlIsInReg(regNumber reg) const
{
switch (vlType)
{
@@ -82,7 +82,7 @@ bool CodeGenInterface::siVarLoc::vlIsInReg(regNumber reg)
}
}
-bool CodeGenInterface::siVarLoc::vlIsOnStk(regNumber reg, signed offset)
+bool CodeGenInterface::siVarLoc::vlIsOnStk(regNumber reg, signed offset) const
{
regNumber actualReg;
@@ -131,30 +131,6 @@ bool CodeGenInterface::siVarLoc::vlIsOnStk(regNumber reg, signed offset)
}
//------------------------------------------------------------------------
-// siVarLoc: Non-empty constructor of siVarLoc struct
-// Arguments:
-// varDsc - a "LclVarDsc *" to the variable it is desired to build the "siVarLoc".
-// baseReg - a "regNumber" use as a base for the offset.
-// offset - a signed amount of bytes distance from "baseReg" for the position of the variable.
-// isFramePointerUsed - a boolean variable
-//
-// Notes:
-// Called for every psiScope in "psiScopeList" codegen.h
-CodeGenInterface::siVarLoc::siVarLoc(const LclVarDsc* varDsc, regNumber baseReg, int offset, bool isFramePointerUsed)
-{
- var_types type = genActualType(varDsc->TypeGet());
-
- if (varDsc->lvIsInReg())
- {
- siFillRegisterVarLoc(varDsc, type, baseReg, offset, isFramePointerUsed);
- }
- else
- {
- siFillStackVarLoc(varDsc, type, baseReg, offset, isFramePointerUsed);
- }
-}
-
-//------------------------------------------------------------------------
// Equals: Compares first reference and then values of the structures.
//
// Arguments:
@@ -228,65 +204,6 @@ bool CodeGenInterface::siVarLoc::Equals(const siVarLoc* lhs, const siVarLoc* rhs
}
//------------------------------------------------------------------------
-// getSiVarLoc: Creates a "CodegenInterface::siVarLoc" instance from using the properties
-// of the "psiScope" instance.
-//
-// Notes:
-// Called for every psiScope in "psiScopeList" codegen.h
-CodeGenInterface::siVarLoc CodeGen::psiScope::getSiVarLoc() const
-{
- CodeGenInterface::siVarLoc varLoc;
-
- if (scRegister)
- {
- varLoc.vlType = VLT_REG;
- varLoc.vlReg.vlrReg = (regNumber)u1.scRegNum;
- }
- else
- {
- varLoc.vlType = VLT_STK;
- varLoc.vlStk.vlsBaseReg = (regNumber)u2.scBaseReg;
- varLoc.vlStk.vlsOffset = u2.scOffset;
- }
-
- return varLoc;
-}
-
-//------------------------------------------------------------------------
-// getSiVarLoc: Returns a "siVarLoc" instance representing the place where the variable
-// is given its description, "baseReg", and "offset" (if needed).
-//
-// Arguments:
-// varDsc - a "LclVarDsc *" to the variable it is desired to build the "siVarLoc".
-// scope - a "siScope" Scope info of the variable.
-//
-// Return Value:
-// A "siVarLoc" filled with the correct case struct fields for the variable, which could live
-// in a register, an stack position, or a combination of both.
-//
-// Notes:
-// Called for each siScope in siScopeList when "genSetScopeInfo".
-CodeGenInterface::siVarLoc CodeGen::getSiVarLoc(const LclVarDsc* varDsc, const siScope* scope) const
-{
- // For stack vars, find the base register, and offset
-
- regNumber baseReg;
- signed offset = varDsc->lvStkOffs;
-
- if (!varDsc->lvFramePointerBased)
- {
- baseReg = REG_SPBASE;
- offset += scope->scStackLevel;
- }
- else
- {
- baseReg = REG_FPBASE;
- }
-
- return CodeGenInterface::siVarLoc(varDsc, baseReg, offset, isFramePointerUsed());
-}
-
-//------------------------------------------------------------------------
// siFillStackVarLoc: Fill "siVarLoc" struct indicating the stack position of the variable
// using "LclVarDsc" and "baseReg"/"offset".
//
@@ -471,6 +388,90 @@ void CodeGenInterface::siVarLoc::siFillRegisterVarLoc(
}
}
+//------------------------------------------------------------------------
+// siVarLoc: Non-empty constructor of siVarLoc struct
+// Arguments:
+// varDsc - a "LclVarDsc *" to the variable it is desired to build the "siVarLoc".
+// baseReg - a "regNumber" use as a base for the offset.
+// offset - a signed amount of bytes distance from "baseReg" for the position of the variable.
+// isFramePointerUsed - a boolean variable
+//
+// Notes:
+// Called for every psiScope in "psiScopeList" codegen.h
+CodeGenInterface::siVarLoc::siVarLoc(const LclVarDsc* varDsc, regNumber baseReg, int offset, bool isFramePointerUsed)
+{
+ var_types type = genActualType(varDsc->TypeGet());
+
+ if (varDsc->lvIsInReg())
+ {
+ siFillRegisterVarLoc(varDsc, type, baseReg, offset, isFramePointerUsed);
+ }
+ else
+ {
+ siFillStackVarLoc(varDsc, type, baseReg, offset, isFramePointerUsed);
+ }
+}
+
+#ifdef USING_SCOPE_INFO
+//------------------------------------------------------------------------
+// getSiVarLoc: Returns a "siVarLoc" instance representing the place where the variable
+// is given its description, "baseReg", and "offset" (if needed).
+//
+// Arguments:
+// varDsc - a "LclVarDsc *" to the variable it is desired to build the "siVarLoc".
+// scope - a "siScope" Scope info of the variable.
+//
+// Return Value:
+// A "siVarLoc" filled with the correct case struct fields for the variable, which could live
+// in a register, an stack position, or a combination of both.
+//
+// Notes:
+// Called for each siScope in siScopeList when "genSetScopeInfo".
+CodeGenInterface::siVarLoc CodeGen::getSiVarLoc(const LclVarDsc* varDsc, const siScope* scope) const
+{
+ // For stack vars, find the base register, and offset
+
+ regNumber baseReg;
+ signed offset = varDsc->lvStkOffs;
+
+ if (!varDsc->lvFramePointerBased)
+ {
+ baseReg = REG_SPBASE;
+ offset += scope->scStackLevel;
+ }
+ else
+ {
+ baseReg = REG_FPBASE;
+ }
+
+ return CodeGenInterface::siVarLoc(varDsc, baseReg, offset, isFramePointerUsed());
+}
+
+//------------------------------------------------------------------------
+// getSiVarLoc: Creates a "CodegenInterface::siVarLoc" instance from using the properties
+// of the "psiScope" instance.
+//
+// Notes:
+// Called for every psiScope in "psiScopeList" codegen.h
+CodeGenInterface::siVarLoc CodeGen::psiScope::getSiVarLoc() const
+{
+ CodeGenInterface::siVarLoc varLoc;
+
+ if (scRegister)
+ {
+ varLoc.vlType = VLT_REG;
+ varLoc.vlReg.vlrReg = (regNumber)u1.scRegNum;
+ }
+ else
+ {
+ varLoc.vlType = VLT_STK;
+ varLoc.vlStk.vlsBaseReg = (regNumber)u2.scBaseReg;
+ varLoc.vlStk.vlsOffset = u2.scOffset;
+ }
+
+ return varLoc;
+}
+
/*============================================================================
*
* Implementation for ScopeInfo
@@ -1613,3 +1614,4 @@ void CodeGen::psiEndProlog()
psiEndPrologScope(scope);
}
}
+#endif // USING_SCOPE_INFO
diff --git a/src/jit/treelifeupdater.cpp b/src/jit/treelifeupdater.cpp
index ecfdaeee86..d5e3ad9a0b 100644
--- a/src/jit/treelifeupdater.cpp
+++ b/src/jit/treelifeupdater.cpp
@@ -245,8 +245,9 @@ void TreeLifeUpdater<ForCodeGen>::UpdateLifeVar(GenTree* tree)
}
#endif // DEBUG
}
-
+#ifdef USING_SCOPE_INFO
compiler->codeGen->siUpdate();
+#endif // USING_SCOPE_INFO
}
}
diff --git a/src/jit/unwindarm.cpp b/src/jit/unwindarm.cpp
index 4a2b838258..936ba1b656 100644
--- a/src/jit/unwindarm.cpp
+++ b/src/jit/unwindarm.cpp
@@ -1780,7 +1780,7 @@ void UnwindInfo::InitUnwindInfo(Compiler* comp, emitLocation* startLoc, emitLoca
// However, its constructor needs to be explicitly called, since the constructor for
// UnwindInfo is not called.
- uwiFragmentFirst.UnwindFragmentInfo::UnwindFragmentInfo(comp, startLoc, false);
+ new (&uwiFragmentFirst, jitstd::placement_t()) UnwindFragmentInfo(comp, startLoc, false);
uwiFragmentLast = &uwiFragmentFirst;
diff --git a/src/jit/utils.cpp b/src/jit/utils.cpp
index 2010678242..888fe605b0 100644
--- a/src/jit/utils.cpp
+++ b/src/jit/utils.cpp
@@ -2153,9 +2153,9 @@ const SignedMagic<int32_t>* TryGetSignedMagic(int32_t divisor)
static const SignedMagic<int32_t> table[]{
{0x55555556, 0}, // 3
{},
- {0x66666667, 1}, // 5
- {0x2aaaaaab, 0}, // 6
- {0x92492493, 2}, // 7
+ {0x66666667, 1}, // 5
+ {0x2aaaaaab, 0}, // 6
+ {(int32_t)0x92492493, 2}, // 7
{},
{0x38e38e39, 1}, // 9
{0x66666667, 2}, // 10
diff --git a/src/nativeresources/rctocpp.awk b/src/nativeresources/rctocpp.awk
index 6f8d597cf4..cef49ce4a5 100644
--- a/src/nativeresources/rctocpp.awk
+++ b/src/nativeresources/rctocpp.awk
@@ -62,7 +62,7 @@ function writeheader(arrayName, tableName)
print "// This code was generated by rctocpp.awk and is not meant to be modified manually."
print "#include <resourcestring.h>";
print "";
- print "extern const NativeStringResourceTable " tableName ";";
+ print "extern NativeStringResourceTable " tableName ";";
print "const NativeStringResource " arrayName "[] = {";
}
@@ -78,7 +78,7 @@ function writefooter(arrayName, tableName)
print "};";
print "";
- print "const NativeStringResourceTable " tableName " __attribute__((visibility(\"default\"))) = {";
+ print "NativeStringResourceTable " tableName " __attribute__((visibility(\"default\"))) = {";
print numEntries ",";
print arrayName "};";
}
diff --git a/src/pal/automation/automation.py b/src/pal/automation/automation.py
deleted file mode 100644
index d8ddaa9117..0000000000
--- a/src/pal/automation/automation.py
+++ /dev/null
@@ -1,78 +0,0 @@
-import logging as log
-import sys
-import getopt
-import os
-import subprocess
-import shutil
-import compile
-import tests
-import util
-
-target = ""
-arch = ""
-platform = ""
-workspace = ""
-fullbuilddirpath = ""
-cleanUp = True
-
-def ParseArgs(argv):
- global target
- global platform
- global arch
- global cleanUp
-
- returncode,target,platform,arch,cleanUp = util.ParseArgs(argv)
- return returncode
-
-def Initialize():
- global workspace
- workspace = util.Initialize(platform)
- return 0
-
-def SetupDirectories():
- global fullbuilddirpath
- fullbuilddirpath = util.SetupDirectories(target, arch, platform)
-
- return 0
-
-def CopyBinaries():
- print "\n==================================================\n"
- print "Stub for copying binaries"
- print "\n==================================================\n"
-
- return 0
-
-def main(argv):
- returncode = ParseArgs(argv)
- if returncode != 0:
- return returncode
-
- returncode += Initialize()
- if returncode != 0:
- return returncode
-
- returncode += SetupDirectories()
- if returncode != 0:
- return returncode
-
- returncode += compile.Compile(workspace, target, platform, arch)
- if returncode != 0:
- return returncode
-
- returncode += CopyBinaries()
- if returncode != 0:
- return returncode
-
- returncode += tests.RunTests(platform, fullbuilddirpath, workspace)
- if returncode != 0:
- return returncode
-
- return returncode
-
-if __name__ == "__main__":
- returncode = main(sys.argv[1:])
-
- util.Cleanup(cleanUp,workspace)
-
- sys.exit(returncode)
-
diff --git a/src/pal/automation/compile.py b/src/pal/automation/compile.py
deleted file mode 100644
index bb9d1b88ac..0000000000
--- a/src/pal/automation/compile.py
+++ /dev/null
@@ -1,70 +0,0 @@
-import logging as log
-import sys
-import getopt
-import os
-import subprocess
-import shutil
-
-def RunCMake(workspace, target, platform):
- # run CMake
- print "\n==================================================\n"
-
- returncode = 0
- if platform == "windows":
- print "Running: vcvarsall.bat x86_amd64 && " + workspace + "\ProjectK\NDP\clr\src\pal\\tools\gen-buildsys-win.bat " + workspace + "\ProjectK\NDP\clr"
- print "\n==================================================\n"
- sys.stdout.flush()
- returncode = subprocess.call(["vcvarsall.bat", "x86_amd64", "&&", workspace + "\ProjectK\NDP\clr\src\pal\\tools\gen-buildsys-win.bat", workspace + "\ProjectK\NDP\clr"])
- elif platform == "linux":
- print "Running: " + workspace + "/ProjectK/NDP/clr/src/pal/tools/gen-buildsys-clang.sh " + workspace + "/ProjectK/NDP/clr DEBUG"
- print "\n==================================================\n"
- sys.stdout.flush()
- returncode = subprocess.call(workspace + "/ProjectK/NDP/clr/src/pal/tools/gen-buildsys-clang.sh " + workspace + "/ProjectK/NDP/clr " + target, shell=True)
-
- if returncode != 0:
- print "ERROR: cmake failed with exit code " + str(returncode)
-
- return returncode
-
-def RunBuild(target, platform, arch):
- if platform == "windows":
- return RunMsBuild(target, arch)
- elif platform == "linux":
- return RunMake()
-
-def RunMsBuild(target, arch):
- # run MsBuild
- print "\n==================================================\n"
- print "Running: vcvarsall.bat x86_amd64 && msbuild CoreCLR.sln /p:Configuration=" + target + " /p:Platform=" + arch
- print "\n==================================================\n"
- sys.stdout.flush()
-
- returncode = subprocess.call(["vcvarsall.bat","x86_amd64","&&","msbuild","CoreCLR.sln","/p:Configuration=" + target,"/p:Platform=" + arch])
-
- if returncode != 0:
- print "ERROR: vcvarsall.bat failed with exit code " + str(returncode)
-
- return returncode
-
-def RunMake():
- print "\n==================================================\n"
- print "Running: make"
- print "\n==================================================\n"
- sys.stdout.flush()
- returncode = subprocess.call(["make"])
-
- if returncode != 0:
- print "ERROR: make failed with exit code " + str(returncode)
-
- return returncode
-
-def Compile(workspace, target, platform, arch):
- returncode = RunCMake(workspace, target, platform)
- if returncode != 0:
- return returncode
-
- returncode += RunBuild(target, platform, arch)
- if returncode != 0:
- return returncode
-
- return returncode
diff --git a/src/pal/automation/tests.py b/src/pal/automation/tests.py
deleted file mode 100644
index e48e84fd1d..0000000000
--- a/src/pal/automation/tests.py
+++ /dev/null
@@ -1,33 +0,0 @@
-import sys
-import getopt
-import os
-import subprocess
-import shutil
-import logging as log
-
-def RunPalTests(fullbuilddirpath, workspace):
- print "\n==================================================\n"
- print "Running PAL Tests."
- print "\n==================================================\n"
-
- print "Running: " + workspace + "/ProjectK/NDP/clr/src/pal/tests/palsuite/runpaltests.sh " + fullbuilddirpath + " " + fullbuilddirpath + "/PalTestOutput"
- print "\n==================================================\n"
- sys.stdout.flush()
- returncode = subprocess.call(workspace + "/ProjectK/NDP/clr/src/pal/tests/palsuite/runpaltests.sh " + fullbuilddirpath + " " + fullbuilddirpath + "/PalTestOutput", shell=True)
-
- if returncode != 0:
- print "ERROR: there were errors failed with exit code " + str(returncode)
-
- return returncode
-
-
-def RunTests(platform, fullbuilddirpath, workspace):
- returncode = 0
-
- if platform == "linux":
- # Execute PAL tests
- returncode = RunPalTests(fullbuilddirpath, workspace)
-
- return returncode
-
-
diff --git a/src/pal/automation/util.py b/src/pal/automation/util.py
deleted file mode 100644
index 790cbc4dbf..0000000000
--- a/src/pal/automation/util.py
+++ /dev/null
@@ -1,97 +0,0 @@
-import sys
-import getopt
-import os
-import subprocess
-import shutil
-import logging as log
-
-def Initialize(platform):
- print "Initializing Workspace"
- global workspace
- workspace = os.environ['WORKSPACE']
- if platform == "windows":
- # Jenkins puts quotes in the path, which is wrong. Remove quotes.
- os.environ['PATH'] = os.environ['PATH'].replace('"','')
-
- return workspace
-
-def ParseArgs(argv):
- print "Parsing arguments for compile"
- try:
- opts, args = getopt.getopt(argv, "t:p:a:v", ["target=", "platform=", "arch=", "verbose","noclean"])
- except getopt.GetoptError:
- print "ERROR: \n\t usage: python compile.py --target <target> --platform <windows|linux> --arch <arch> [--verbose] [--noclean]"
- return 2,"","","",True
-
- verbose = False
- cleanUp = True
-
- acceptedPlatforms = ['windows','linux']
-
- for opt, arg in opts:
- if opt in ("-t", "--target"):
- target = arg
- elif opt in ("-p", "--platform"):
- if arg.lower() not in acceptedPlatforms:
- print "ERROR: " + arg + "not an accepted platform. Use windows or linux."
- sys.exit(2)
- platform = arg.lower()
- elif opt in ("-a", "--arch"):
- arch = arg
- elif opt in ("-v", "--verbose"):
- verbose = True
- elif opt in ("-c", "--noclean"):
- cleanUp = False
-
- if verbose:
- log.basicConfig(format="%(levelname)s: %(message)s", level=log.DEBUG)
- log.info("In verbose mode.")
- else:
- log.basicConfig(format="%(levelname)s: %(message)s")
-
- if target == "" or platform == "" or arch == "":
- # must specify target, project and arch
- log.error("Must specify target, project and arch")
- return 2,"","","",True
-
- return 0,target,platform,arch,cleanUp
-
-def SetupDirectories(target, arch, platform):
- log.info("Setting up directories")
-
- global rootdir
- global builddir
- global fullBuildDirPath
-
- rootdir = "build"
- if not os.path.isdir(rootdir):
- os.mkdir(rootdir)
- os.chdir(rootdir)
-
- builddir = "build-" + platform
-
- if platform == "windows":
- builddir = builddir + "-" + arch + "-" + target
-
- if os.path.isdir(builddir):
- shutil.rmtree(builddir)
- os.mkdir(builddir)
- os.chdir(builddir)
-
- fullbuilddirpath = workspace + "/" + rootdir + "/" + builddir
-
- return fullbuilddirpath
-
-def Cleanup(cleanUp,workspace):
- print "\n==================================================\n"
- print "Cleaning Up."
- print "\n==================================================\n"
-
- if cleanUp:
- os.chdir(workspace + "/" + rootdir)
- shutil.rmtree(builddir)
- os.chdir("..")
- shutil.rmtree(rootdir)
-
- log.shutdown()
- return 0
diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h
index 26c9e3f6d8..38ef778d53 100644
--- a/src/pal/inc/pal.h
+++ b/src/pal/inc/pal.h
@@ -475,7 +475,18 @@ PALAPI
PAL_GetApplicationGroupId();
#endif
-static const int MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH = MAX_PATH;
+static const unsigned int MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH = MAX_PATH;
+
+PALIMPORT
+VOID
+PALAPI
+PAL_GetTransportName(
+ const unsigned int MAX_TRANSPORT_NAME_LENGTH,
+ OUT char *name,
+ IN const char *prefix,
+ IN DWORD id,
+ IN const char *applicationGroupId,
+ IN const char *suffix);
PALIMPORT
VOID
diff --git a/src/pal/src/misc/cgroup.cpp b/src/pal/src/misc/cgroup.cpp
index 304b813f28..144ac669df 100644
--- a/src/pal/src/misc/cgroup.cpp
+++ b/src/pal/src/misc/cgroup.cpp
@@ -386,20 +386,18 @@ size_t
PALAPI
PAL_GetRestrictedPhysicalMemoryLimit()
{
- size_t physical_memory_limit;
+ size_t physical_memory_limit = 0;
if (!CGroup::GetPhysicalMemoryLimit(&physical_memory_limit))
return 0;
- else
+
+ // If there's no memory limit specified on the container this
+ // actually returns 0x7FFFFFFFFFFFF000 (2^63-1 rounded down to
+ // 4k which is a common page size). So we know we are not
+ // running in a memory restricted environment.
+ if (physical_memory_limit > 0x7FFFFFFF00000000)
{
- // If there's no memory limit specified on the container this
- // actually returns 0x7FFFFFFFFFFFF000 (2^63-1 rounded down to
- // 4k which is a common page size). So we know we are not
- // running in a memory restricted environment.
- if (physical_memory_limit > 0x7FFFFFFF00000000)
- {
- return 0;
- }
+ return 0;
}
struct rlimit curr_rlimit;
diff --git a/src/pal/src/misc/sysinfo.cpp b/src/pal/src/misc/sysinfo.cpp
index 495cc8bb94..2c14949b95 100644
--- a/src/pal/src/misc/sysinfo.cpp
+++ b/src/pal/src/misc/sysinfo.cpp
@@ -102,7 +102,17 @@ PAL_GetLogicalCpuCountFromOS()
{
int nrcpus = 0;
-#if HAVE_SYSCONF
+#if HAVE_SCHED_GETAFFINITY
+
+ cpu_set_t cpuSet;
+ int st = sched_getaffinity(0, sizeof(cpu_set_t), &cpuSet);
+ if (st != 0)
+ {
+ ASSERT("sched_getaffinity failed (%d)\n", errno);
+ }
+
+ nrcpus = CPU_COUNT(&cpuSet);
+#elif HAVE_SYSCONF
#if defined(_ARM_) || defined(_ARM64_)
#define SYSCONF_GET_NUMPROCS _SC_NPROCESSORS_CONF
diff --git a/src/pal/src/numa/numa.cpp b/src/pal/src/numa/numa.cpp
index 8793a09b85..9283a044da 100644
--- a/src/pal/src/numa/numa.cpp
+++ b/src/pal/src/numa/numa.cpp
@@ -166,7 +166,7 @@ the processors enabled.
--*/
KAFFINITY GetFullAffinityMask(int cpuCount)
{
- if (cpuCount < sizeof(KAFFINITY) * 8)
+ if ((size_t)cpuCount < sizeof(KAFFINITY) * 8)
{
return ((KAFFINITY)1 << (cpuCount)) - 1;
}
diff --git a/src/pal/src/thread/process.cpp b/src/pal/src/thread/process.cpp
index 5615036806..35ac2bf442 100644
--- a/src/pal/src/thread/process.cpp
+++ b/src/pal/src/thread/process.cpp
@@ -1549,7 +1549,8 @@ static uint64_t HashSemaphoreName(uint64_t a, uint64_t b)
#define HashSemaphoreName(a,b) a,b
#endif
-static const char* PipeNameFormat = "clr-debug-pipe-%d-%llu-%s";
+static const char *const TwoWayNamedPipePrefix = "clr-debug-pipe";
+static const char* IpcNameFormat = "%s-%d-%llu-%s";
class PAL_RuntimeStartupHelper
{
@@ -2288,14 +2289,16 @@ GetProcessIdDisambiguationKey(DWORD processId, UINT64 *disambiguationKey)
/*++
Function:
- PAL_GetTransportPipeName
+ PAL_GetTransportName
- Builds the transport pipe names from the process id.
+ Builds the transport IPC names from the process id.
--*/
VOID
PALAPI
-PAL_GetTransportPipeName(
+PAL_GetTransportName(
+ const unsigned int MAX_TRANSPORT_NAME_LENGTH,
OUT char *name,
+ IN const char *prefix,
IN DWORD id,
IN const char *applicationGroupId,
IN const char *suffix)
@@ -2305,7 +2308,7 @@ PAL_GetTransportPipeName(
UINT64 disambiguationKey = 0;
PathCharString formatBufferString;
BOOL ret = GetProcessIdDisambiguationKey(id, &disambiguationKey);
- char *formatBuffer = formatBufferString.OpenStringBuffer(MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH-1);
+ char *formatBuffer = formatBufferString.OpenStringBuffer(MAX_TRANSPORT_NAME_LENGTH-1);
if (formatBuffer == nullptr)
{
ERROR("Out Of Memory");
@@ -2337,9 +2340,9 @@ PAL_GetTransportPipeName(
}
// Verify the size of the path won't exceed maximum allowed size
- if (formatBufferString.GetCount() >= MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH)
+ if (formatBufferString.GetCount() >= MAX_TRANSPORT_NAME_LENGTH)
{
- ERROR("GetApplicationContainerFolder returned a path that was larger than MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH");
+ ERROR("GetApplicationContainerFolder returned a path that was larger than MAX_TRANSPORT_NAME_LENGTH");
return;
}
}
@@ -2347,27 +2350,50 @@ PAL_GetTransportPipeName(
#endif // __APPLE__
{
// Get a temp file location
- dwRetVal = ::GetTempPathA(MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH, formatBuffer);
+ dwRetVal = ::GetTempPathA(MAX_TRANSPORT_NAME_LENGTH, formatBuffer);
if (dwRetVal == 0)
{
ERROR("GetTempPath failed (0x%08x)", ::GetLastError());
return;
}
- if (dwRetVal > MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH)
+ if (dwRetVal > MAX_TRANSPORT_NAME_LENGTH)
{
- ERROR("GetTempPath returned a path that was larger than MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH");
+ ERROR("GetTempPath returned a path that was larger than MAX_TRANSPORT_NAME_LENGTH");
return;
}
}
- if (strncat_s(formatBuffer, MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH, PipeNameFormat, strlen(PipeNameFormat)) == STRUNCATE)
+ if (strncat_s(formatBuffer, MAX_TRANSPORT_NAME_LENGTH, IpcNameFormat, strlen(IpcNameFormat)) == STRUNCATE)
{
- ERROR("TransportPipeName was larger than MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH");
+ ERROR("TransportPipeName was larger than MAX_TRANSPORT_NAME_LENGTH");
return;
}
- int chars = snprintf(name, MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH, formatBuffer, id, disambiguationKey, suffix);
- _ASSERTE(chars > 0 && chars < MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH);
+ int chars = snprintf(name, MAX_TRANSPORT_NAME_LENGTH, formatBuffer, prefix, id, disambiguationKey, suffix);
+ _ASSERTE(chars > 0 && (unsigned int)chars < MAX_TRANSPORT_NAME_LENGTH);
+}
+
+/*++
+ Function:
+ PAL_GetTransportPipeName
+
+ Builds the transport pipe names from the process id.
+--*/
+VOID
+PALAPI
+PAL_GetTransportPipeName(
+ OUT char *name,
+ IN DWORD id,
+ IN const char *applicationGroupId,
+ IN const char *suffix)
+{
+ PAL_GetTransportName(
+ MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH,
+ name,
+ TwoWayNamedPipePrefix,
+ id,
+ applicationGroupId,
+ suffix);
}
/*++
diff --git a/src/pal/tools/gen-buildsys-clang.sh b/src/pal/tools/gen-buildsys-clang.sh
index 95b227d89f..8f9881ba0c 100755
--- a/src/pal/tools/gen-buildsys-clang.sh
+++ b/src/pal/tools/gen-buildsys-clang.sh
@@ -3,13 +3,14 @@
# This file invokes cmake and generates the build system for Clang.
#
-if [ $# -lt 4 ]
+if [ $# -lt 5 ]
then
echo "Usage..."
- echo "gen-buildsys-clang.sh <path to top level CMakeLists.txt> <ClangMajorVersion> <ClangMinorVersion> <Architecture> [build flavor] [coverage] [ninja] [scan-build] [cmakeargs]"
+ echo "gen-buildsys-clang.sh <path to top level CMakeLists.txt> <ClangMajorVersion> <ClangMinorVersion> <Architecture> <ScriptDirectory> [build flavor] [coverage] [ninja] [scan-build] [cmakeargs]"
echo "Specify the path to the top level CMake file - <ProjectK>/src/NDP"
echo "Specify the clang version to use, split into major and minor version"
- echo "Specify the target architecture."
+ echo "Specify the target architecture."
+ echo "Specify the script directory."
echo "Optionally specify the build configuration (flavor.) Defaults to DEBUG."
echo "Optionally specify 'coverage' to enable code coverage build."
echo "Optionally specify 'scan-build' to enable build with clang static analyzer."
@@ -40,6 +41,7 @@ export CC="$(command -v clang$desired_llvm_version)"
export CXX="$(command -v clang++$desired_llvm_version)"
build_arch="$4"
+script_dir="$5"
buildtype=DEBUG
code_coverage=OFF
build_tests=OFF
@@ -47,7 +49,7 @@ scan_build=OFF
generator="Unix Makefiles"
__UnprocessedCMakeArgs=""
-for i in "${@:5}"; do
+for i in "${@:6}"; do
upperI="$(echo $i | awk '{print toupper($0)}')"
case $upperI in
# Possible build types are DEBUG, CHECKED, RELEASE, RELWITHDEBINFO, MINSIZEREL.
@@ -155,8 +157,7 @@ else
overridefile=clang-compiler-override.txt
fi
-# Determine the current script directory
-__currentScriptDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+__currentScriptDir="$script_dir"
cmake_command=cmake
diff --git a/src/pal/tools/gen-buildsys-gcc.sh b/src/pal/tools/gen-buildsys-gcc.sh
index 0d66368048..2faf29a887 100755
--- a/src/pal/tools/gen-buildsys-gcc.sh
+++ b/src/pal/tools/gen-buildsys-gcc.sh
@@ -3,13 +3,14 @@
# This file invokes cmake and generates the build system for Gcc.
#
-if [ $# -lt 4 ]
+if [ $# -lt 5 ]
then
echo "Usage..."
- echo "gen-buildsys-gcc.sh <path to top level CMakeLists.txt> <GccMajorVersion> <GccMinorVersion> <Architecture> [build flavor] [coverage] [ninja] [cmakeargs]"
+ echo "gen-buildsys-gcc.sh <path to top level CMakeLists.txt> <GccMajorVersion> <GccMinorVersion> <Architecture> <ScriptDirectory> [build flavor] [coverage] [ninja] [cmakeargs]"
echo "Specify the path to the top level CMake file - <ProjectK>/src/NDP"
echo "Specify the Gcc version to use, split into major and minor version"
echo "Specify the target architecture."
+ echo "Specify the script directory."
echo "Optionally specify the build configuration (flavor.) Defaults to DEBUG."
echo "Optionally specify 'coverage' to enable code coverage build."
echo "Target ninja instead of make. ninja must be on the PATH."
@@ -60,6 +61,7 @@ fi
export CC CXX
build_arch="$4"
+script_dir="$5"
buildtype=DEBUG
code_coverage=OFF
generator="Unix Makefiles"
@@ -68,7 +70,7 @@ __UnprocessedCMakeArgs=""
ITER=-1
for i in "$@"; do
ITER=$((ITER + 1))
- if [ $ITER -lt 5 ]; then continue; fi
+ if [ $ITER -lt 6 ]; then continue; fi
upperI="$(echo "$i" | awk '{print toupper($0)}')"
case $upperI in
# Possible build types are DEBUG, CHECKED, RELEASE, RELWITHDEBINFO, MINSIZEREL.
@@ -158,8 +160,7 @@ fi
overridefile=gcc-compiler-override.txt
-# Determine the current script directory
-__currentScriptDir="$(cd -- "$(dirname -- "$0")" && pwd -P)"
+__currentScriptDir="$script_dir"
cmake \
-G "$generator" \
diff --git a/src/scripts/genEtwProvider.py b/src/scripts/genEtwProvider.py
index 79a286a501..89d3119cf4 100644
--- a/src/scripts/genEtwProvider.py
+++ b/src/scripts/genEtwProvider.py
@@ -14,7 +14,7 @@ import argparse
import subprocess
import xml.dom.minidom as DOM
from genEventing import parseTemplateNodes
-from utilities import open_for_update, update_directory
+from utilities import open_for_update, update_directory, parseExclusionList
macroheader_filename = "etwmacros.h"
mcheader_filename = "ClrEtwAll.h"
@@ -102,59 +102,12 @@ def genXplatHeader(intermediate):
#endif //_CLR_XPLAT_EVENTS_H_
""".format(etw_dirname, macroheader_filename, mcheader_filename))
-
-class EventExclusions:
- def __init__(self):
- self.nostack = set()
- self.explicitstack = set()
- self.noclrinstance = set()
-
-def parseExclusionList(exclusion_filename):
- with open(exclusion_filename,'r') as ExclusionFile:
- exclusionInfo = EventExclusions()
-
- for line in ExclusionFile:
- line = line.strip()
-
- #remove comments
- if not line or line.startswith('#'):
- continue
-
- tokens = line.split(':')
- #entries starting with nomac are ignored
- if "nomac" in tokens:
- continue
-
- if len(tokens) > 5:
- raise Exception("Invalid Entry " + line + "in "+ exclusion_filename)
-
- eventProvider = tokens[2]
- eventTask = tokens[1]
- eventSymbol = tokens[4]
-
- if eventProvider == '':
- eventProvider = "*"
- if eventTask == '':
- eventTask = "*"
- if eventSymbol == '':
- eventSymbol = "*"
- entry = eventProvider + ":" + eventTask + ":" + eventSymbol
-
- if tokens[0].lower() == "nostack":
- exclusionInfo.nostack.add(entry)
- if tokens[0].lower() == "stack":
- exclusionInfo.explicitstack.add(entry)
- if tokens[0].lower() == "noclrinstanceid":
- exclusionInfo.noclrinstance.add(entry)
-
- return exclusionInfo
-
def getStackWalkBit(eventProvider, taskName, eventSymbol, stackSet):
for entry in stackSet:
tokens = entry.split(':')
if len(tokens) != 3:
- raise Exception("Error, possible error in the script which introduced the enrty "+ entry)
+ raise Exception("Error, possible error in the script which introduced the entry "+ entry)
eventCond = tokens[0] == eventProvider or tokens[0] == "*"
taskCond = tokens[1] == taskName or tokens[1] == "*"
@@ -164,7 +117,7 @@ def getStackWalkBit(eventProvider, taskName, eventSymbol, stackSet):
return False
return True
-#Add the miscelaneous checks here
+#Add the miscellaneous checks here
def checkConsistency(manifest, exclusion_filename):
tree = DOM.parse(manifest)
exclusionInfo = parseExclusionList(exclusion_filename)
diff --git a/src/scripts/genEventPipe.py b/src/scripts/genEventPipe.py
index 59f5d711fe..f66b54ea28 100644
--- a/src/scripts/genEventPipe.py
+++ b/src/scripts/genEventPipe.py
@@ -3,7 +3,7 @@ from genEventing import *
from genLttngProvider import *
import os
import xml.dom.minidom as DOM
-from utilities import open_for_update
+from utilities import open_for_update, parseExclusionList
stdprolog_cpp = """// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
@@ -73,7 +73,7 @@ def generateMethodSignatureWrite(eventName, template, extern):
return ''.join(sig_pieces)
def generateClrEventPipeWriteEventsImpl(
- providerName, eventNodes, allTemplates, extern):
+ providerName, eventNodes, allTemplates, extern, exclusionList):
providerPrettyName = providerName.replace("Windows-", '')
providerPrettyName = providerPrettyName.replace("Microsoft-", '')
providerPrettyName = providerPrettyName.replace('-', '_')
@@ -153,8 +153,14 @@ def generateClrEventPipeWriteEventsImpl(
eventLevel = eventLevel.replace("win:", "EventPipeEventLevel::")
taskName = eventNode.getAttribute('task')
- initEvent = """ EventPipeEvent%s = EventPipeProvider%s->AddEvent(%s,%s,%s,%s);
-""" % (eventName, providerPrettyName, eventValue, eventKeywordsMask, eventVersion, eventLevel)
+ needStack = "true"
+ for nostackentry in exclusionList.nostack:
+ tokens = nostackentry.split(':')
+ if tokens[2] == eventName:
+ needStack = "false"
+
+ initEvent = """ EventPipeEvent%s = EventPipeProvider%s->AddEvent(%s,%s,%s,%s,%s);
+""" % (eventName, providerPrettyName, eventValue, eventKeywordsMask, eventVersion, eventLevel, needStack)
WriteEventImpl.append(initEvent)
WriteEventImpl.append("}")
@@ -384,7 +390,7 @@ bool WriteToBuffer(const char *str, char *&buffer, size_t& offset, size_t& size,
helper.close()
def generateEventPipeImplFiles(
- etwmanifest, eventpipe_directory, extern):
+ etwmanifest, eventpipe_directory, extern, exclusionList):
tree = DOM.parse(etwmanifest)
# Find the src directory starting with the assumption that
@@ -460,11 +466,12 @@ bool WriteToBuffer(const T &value, char *&buffer, size_t& offset, size_t& size,
providerName,
eventNodes,
allTemplates,
- extern) + "\n")
+ extern,
+ exclusionList) + "\n")
def generateEventPipeFiles(
- etwmanifest, intermediate, extern):
+ etwmanifest, intermediate, extern, exclusionList):
eventpipe_directory = os.path.join(intermediate, eventpipe_dirname)
tree = DOM.parse(etwmanifest)
@@ -487,7 +494,8 @@ def generateEventPipeFiles(
generateEventPipeImplFiles(
etwmanifest,
eventpipe_directory,
- extern
+ extern,
+ exclusionList
)
import argparse
@@ -502,6 +510,8 @@ def main(argv):
required = parser.add_argument_group('required arguments')
required.add_argument('--man', type=str, required=True,
help='full path to manifest containig the description of events')
+ required.add_argument('--exc', type=str, required=True,
+ help='full path to exclusion list')
required.add_argument('--intermediate', type=str, required=True,
help='full path to eventprovider intermediate directory')
required.add_argument('--nonextern', action='store_true',
@@ -512,10 +522,11 @@ def main(argv):
return 1
sClrEtwAllMan = args.man
+ exclusion_filename = args.exc
intermediate = args.intermediate
extern = not args.nonextern
- generateEventPipeFiles(sClrEtwAllMan, intermediate, extern)
+ generateEventPipeFiles(sClrEtwAllMan, intermediate, extern, parseExclusionList(exclusion_filename))
if __name__ == '__main__':
return_code = main(sys.argv[1:])
diff --git a/src/scripts/utilities.py b/src/scripts/utilities.py
index 7e69f462a9..c4767a43b7 100644
--- a/src/scripts/utilities.py
+++ b/src/scripts/utilities.py
@@ -120,3 +120,49 @@ def update_directory(srcpath, dstpath, recursive=True, destructive=True, shallow
if not os.path.exists(dstdir):
os.makedirs(dstdir)
update_directory(srcdir, dstdir, recursive, destructive, shallow)
+
+class EventExclusions:
+ def __init__(self):
+ self.nostack = set()
+ self.explicitstack = set()
+ self.noclrinstance = set()
+
+def parseExclusionList(exclusion_filename):
+ with open(exclusion_filename,'r') as ExclusionFile:
+ exclusionInfo = EventExclusions()
+
+ for line in ExclusionFile:
+ line = line.strip()
+
+ #remove comments
+ if not line or line.startswith('#'):
+ continue
+
+ tokens = line.split(':')
+ #entries starting with nomac are ignored
+ if "nomac" in tokens:
+ continue
+
+ if len(tokens) > 5:
+ raise Exception("Invalid Entry " + line + "in "+ exclusion_filename)
+
+ eventProvider = tokens[2]
+ eventTask = tokens[1]
+ eventSymbol = tokens[4]
+
+ if eventProvider == '':
+ eventProvider = "*"
+ if eventTask == '':
+ eventTask = "*"
+ if eventSymbol == '':
+ eventSymbol = "*"
+ entry = eventProvider + ":" + eventTask + ":" + eventSymbol
+
+ if tokens[0].lower() == "nostack":
+ exclusionInfo.nostack.add(entry)
+ if tokens[0].lower() == "stack":
+ exclusionInfo.explicitstack.add(entry)
+ if tokens[0].lower() == "noclrinstanceid":
+ exclusionInfo.noclrinstance.add(entry)
+
+ return exclusionInfo \ No newline at end of file
diff --git a/src/strongname/api/common.h b/src/strongname/api/common.h
index 26c545cff9..626d9bb720 100644
--- a/src/strongname/api/common.h
+++ b/src/strongname/api/common.h
@@ -146,6 +146,9 @@ typedef DPTR(class ReJitManager) PTR_ReJitManager;
typedef DPTR(struct ReJitInfo) PTR_ReJitInfo;
typedef DPTR(struct SharedReJitInfo) PTR_SharedReJitInfo;
typedef DPTR(class StringObject) PTR_StringObject;
+#ifdef FEATURE_UTF8STRING
+typedef DPTR(class Utf8StringObject) PTR_Utf8StringObject;
+#endif // FEATURE_UTF8STRING
typedef DPTR(class TypeHandle) PTR_TypeHandle;
#ifdef STUB_DISPATCH
typedef VPTR(class VirtualCallStubManager) PTR_VirtualCallStubManager;
diff --git a/src/tools/r2rdump/DisassemblingTypeProvider.cs b/src/tools/r2rdump/DisassemblingTypeProvider.cs
index 75c03265a5..19621f16c0 100644
--- a/src/tools/r2rdump/DisassemblingTypeProvider.cs
+++ b/src/tools/r2rdump/DisassemblingTypeProvider.cs
@@ -34,51 +34,17 @@ namespace R2RDump
public virtual string GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind = 0)
{
- TypeDefinition definition = reader.GetTypeDefinition(handle);
-
- string name = definition.Namespace.IsNil
- ? reader.GetString(definition.Name)
- : reader.GetString(definition.Namespace) + "." + reader.GetString(definition.Name);
-
- if ((definition.Attributes & TypeAttributes.NestedPublic) != 0 || (definition.Attributes & TypeAttributes.NestedFamily) != 0)
- {
- TypeDefinitionHandle declaringTypeHandle = definition.GetDeclaringType();
- return GetTypeFromDefinition(reader, declaringTypeHandle, 0) + "." + name;
- }
-
- return name;
+ return MetadataNameFormatter.FormatHandle(reader, handle);
}
public virtual string GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind = 0)
{
- TypeReference reference = reader.GetTypeReference(handle);
- Handle scope = reference.ResolutionScope;
-
- string name = reference.Namespace.IsNil
- ? reader.GetString(reference.Name)
- : reader.GetString(reference.Namespace) + "." + reader.GetString(reference.Name);
-
- switch (scope.Kind)
- {
- case HandleKind.ModuleReference:
- return "[.module " + reader.GetString(reader.GetModuleReference((ModuleReferenceHandle)scope).Name) + "]" + name;
-
- case HandleKind.AssemblyReference:
- var assemblyReferenceHandle = (AssemblyReferenceHandle)scope;
- var assemblyReference = reader.GetAssemblyReference(assemblyReferenceHandle);
- return "[" + reader.GetString(assemblyReference.Name) + "]" + name;
-
- case HandleKind.TypeReference:
- return GetTypeFromReference(reader, (TypeReferenceHandle)scope) + "+" + name;
-
- default:
- return name;
- }
+ return MetadataNameFormatter.FormatHandle(reader, handle);
}
public virtual string GetTypeFromSpecification(MetadataReader reader, DisassemblingGenericContext genericContext, TypeSpecificationHandle handle, byte rawTypeKind = 0)
{
- return reader.GetTypeSpecification(handle).DecodeSignature(this, genericContext);
+ return MetadataNameFormatter.FormatHandle(reader, handle);
}
public virtual string GetSZArrayType(string elementType)
@@ -161,20 +127,7 @@ namespace R2RDump
public virtual string GetTypeFromHandle(MetadataReader reader, DisassemblingGenericContext genericContext, EntityHandle handle)
{
- switch (handle.Kind)
- {
- case HandleKind.TypeDefinition:
- return GetTypeFromDefinition(reader, (TypeDefinitionHandle)handle);
-
- case HandleKind.TypeReference:
- return GetTypeFromReference(reader, (TypeReferenceHandle)handle);
-
- case HandleKind.TypeSpecification:
- return GetTypeFromSpecification(reader, genericContext, (TypeSpecificationHandle)handle);
-
- default:
- throw new ArgumentOutOfRangeException(nameof(handle));
- }
+ return MetadataNameFormatter.FormatHandle(reader, handle);
}
public virtual string GetModifiedType(string modifierType, string unmodifiedType, bool isRequired)
diff --git a/src/tools/r2rdump/R2RDump.cs b/src/tools/r2rdump/R2RDump.cs
index 1ad800bc7b..07e329880b 100644
--- a/src/tools/r2rdump/R2RDump.cs
+++ b/src/tools/r2rdump/R2RDump.cs
@@ -449,7 +449,7 @@ namespace R2RDump
foreach (string filename in _inputFilenames)
{
// parse the ReadyToRun image
- R2RReader r2r = new R2RReader(filename);
+ R2RReader r2r = new R2RReader(_options, filename);
if (_options.Disasm)
{
diff --git a/src/tools/r2rdump/R2RReader.cs b/src/tools/r2rdump/R2RReader.cs
index 3e38adc799..62bf8a1f7b 100644
--- a/src/tools/r2rdump/R2RReader.cs
+++ b/src/tools/r2rdump/R2RReader.cs
@@ -78,6 +78,11 @@ namespace R2RDump
public class R2RReader
{
/// <summary>
+ /// Option are used to specify details of signature formatting.
+ /// </summary>
+ public readonly DumpOptions Options;
+
+ /// <summary>
/// Underlying PE image reader is used to access raw PE structures like header
/// or section list.
/// </summary>
@@ -178,8 +183,9 @@ namespace R2RDump
/// </summary>
/// <param name="filename">PE image</param>
/// <exception cref="BadImageFormatException">The Cor header flag must be ILLibrary</exception>
- public unsafe R2RReader(string filename)
+ public unsafe R2RReader(DumpOptions options, string filename)
{
+ Options = options;
Filename = filename;
Image = File.ReadAllBytes(filename);
@@ -370,7 +376,7 @@ namespace R2RDump
NativeParser curParser = allEntriesEnum.GetNext();
while (!curParser.IsNull())
{
- SignatureDecoder decoder = new SignatureDecoder(this, (int)curParser.Offset);
+ SignatureDecoder decoder = new SignatureDecoder(Options, this, (int)curParser.Offset);
string owningType = null;
@@ -634,7 +640,7 @@ namespace R2RDump
long section = NativeReader.ReadInt64(Image, ref sectionOffset);
uint sigRva = NativeReader.ReadUInt32(Image, ref signatureOffset);
int sigOffset = GetOffset((int)sigRva);
- string cellName = MetadataNameFormatter.FormatSignature(this, sigOffset);
+ string cellName = MetadataNameFormatter.FormatSignature(Options, this, sigOffset);
entries.Add(new R2RImportSection.ImportSectionEntry(entries.Count, entryOffset, entryOffset + rva, section, sigRva, cellName));
ImportCellNames.Add(rva + entrySize * i, cellName);
}
diff --git a/src/tools/r2rdump/R2RSignature.cs b/src/tools/r2rdump/R2RSignature.cs
index f999aca46e..60e4c0fd0a 100644
--- a/src/tools/r2rdump/R2RSignature.cs
+++ b/src/tools/r2rdump/R2RSignature.cs
@@ -41,9 +41,9 @@ namespace R2RDump
return formatter.EmitHandleName(handle, namespaceQualified, owningTypeOverride);
}
- public static string FormatSignature(R2RReader r2rReader, int imageOffset)
+ public static string FormatSignature(DumpOptions options, R2RReader r2rReader, int imageOffset)
{
- SignatureDecoder decoder = new SignatureDecoder(r2rReader, imageOffset);
+ SignatureDecoder decoder = new SignatureDecoder(options, r2rReader, imageOffset);
string result = decoder.ReadR2RSignature();
return result;
}
@@ -142,7 +142,7 @@ namespace R2RDump
builder.Append(" ");
if (owningTypeOverride == null)
{
- owningTypeOverride = EmitHandleName(methodDef.GetDeclaringType(), namespaceQualified: false, owningTypeOverride: null);
+ owningTypeOverride = EmitHandleName(methodDef.GetDeclaringType(), namespaceQualified: true, owningTypeOverride: null);
}
builder.Append(owningTypeOverride);
builder.Append(".");
@@ -296,6 +296,11 @@ namespace R2RDump
private readonly MetadataReader _metadataReader;
/// <summary>
+ /// Dump options are used to specify details of signature formatting.
+ /// </summary>
+ private readonly DumpOptions _options;
+
+ /// <summary>
/// Byte array representing the R2R PE file read from disk.
/// </summary>
private readonly byte[] _image;
@@ -315,10 +320,12 @@ namespace R2RDump
/// </summary>
/// <param name="reader">R2RReader object representing the R2R PE file</param>
/// <param name="offset">Signature offset within the array</param>
- public SignatureDecoder(R2RReader reader, int offset)
+ /// <param name="options">Formatting options</param>
+ public SignatureDecoder(DumpOptions options, R2RReader reader, int offset)
{
- _image = reader.Image;
_metadataReader = reader.MetadataReader;
+ _options = options;
+ _image = reader.Image;
_offset = offset;
}
@@ -328,10 +335,11 @@ namespace R2RDump
/// <param name="metadataReader">Metadata reader for the R2R image</param>
/// <param name="signature">Signature to parse</param>
/// <param name="offset">Optional signature offset within the signature byte array, 0 by default</param>
- public SignatureDecoder(MetadataReader metadataReader, byte[] signature, int offset = 0)
+ public SignatureDecoder(DumpOptions options, MetadataReader metadataReader, byte[] signature, int offset = 0)
{
- _image = signature;
_metadataReader = metadataReader;
+ _options = options;
+ _image = signature;
_offset = offset;
}
@@ -516,12 +524,14 @@ namespace R2RDump
{
ParseMethodDefToken(builder, owningTypeOverride: null);
}
- builder.Append(" (METHOD_ENTRY_DEF_TOKEN)");
+ builder.Append(" (METHOD_ENTRY");
+ builder.Append(_options.Naked ? ")" : "_DEF_TOKEN)");
break;
case ReadyToRunFixupKind.READYTORUN_FIXUP_MethodEntry_RefToken:
ParseMethodRefToken(builder, owningTypeOverride: null);
- builder.Append(" (METHOD_ENTRY_REF_TOKEN)");
+ builder.Append(" (METHOD_ENTRY");
+ builder.Append(_options.Naked ? ")" : "_REF_TOKEN)");
break;
@@ -535,12 +545,14 @@ namespace R2RDump
case ReadyToRunFixupKind.READYTORUN_FIXUP_VirtualEntry_DefToken:
ParseMethodDefToken(builder, owningTypeOverride: null);
- builder.Append(" (VIRTUAL_ENTRY_DEF_TOKEN)");
+ builder.Append(" (VIRTUAL_ENTRY");
+ builder.Append(_options.Naked ? ")" : "_DEF_TOKEN)");
break;
case ReadyToRunFixupKind.READYTORUN_FIXUP_VirtualEntry_RefToken:
ParseMethodRefToken(builder, owningTypeOverride: null);
- builder.Append(" (VIRTUAL_ENTRY_REF_TOKEN)");
+ builder.Append(" (VIRTUAL_ENTRY");
+ builder.Append(_options.Naked ? ")" : "_REF_TOKEN)");
break;
case ReadyToRunFixupKind.READYTORUN_FIXUP_VirtualEntry_Slot:
@@ -622,12 +634,13 @@ namespace R2RDump
case ReadyToRunFixupKind.READYTORUN_FIXUP_FieldBaseOffset:
- builder.Append("FIELD_BASE_OFFSET");
- // TODO
+ ParseType(builder);
+ builder.Append(" (FIELD_BASE_OFFSET)");
break;
case ReadyToRunFixupKind.READYTORUN_FIXUP_FieldOffset:
- builder.Append("FIELD_OFFSET");
+ ParseField(builder);
+ builder.Append(" (FIELD_OFFSET)");
// TODO
break;
@@ -915,7 +928,7 @@ namespace R2RDump
string owningTypeOverride = null;
if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_OwnerType) != 0)
{
- SignatureDecoder owningTypeDecoder = new SignatureDecoder(_metadataReader, _image, _offset);
+ SignatureDecoder owningTypeDecoder = new SignatureDecoder(_options, _metadataReader, _image, _offset);
owningTypeOverride = owningTypeDecoder.ReadTypeSignature();
_offset = owningTypeDecoder._offset;
}
diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt
index 397f4e2ea5..b7ae17a156 100644
--- a/src/vm/CMakeLists.txt
+++ b/src/vm/CMakeLists.txt
@@ -306,6 +306,7 @@ set(VM_SOURCES_WKS
comwaithandle.cpp
customattribute.cpp
custommarshalerinfo.cpp
+ diagnosticserver.cpp
dllimportcallback.cpp
eeconfig.cpp
eecontract.cpp
@@ -319,12 +320,15 @@ set(VM_SOURCES_WKS
eventpipeeventsource.cpp
eventpipeblock.cpp
eventpipefile.cpp
+ eventpipeinternal.cpp
eventpipejsonfile.cpp
eventpipemetadatagenerator.cpp
+ eventpipeprotocolhelper.cpp
eventpipeprovider.cpp
eventpipebuffer.cpp
eventpipebuffermanager.cpp
eventpipesession.cpp
+ eventpipesessionprovider.cpp
eventstore.cpp
fastserializer.cpp
fcall.cpp
@@ -421,6 +425,8 @@ set(VM_HEADERS_WKS
comwaithandle.h
customattribute.h
custommarshalerinfo.h
+ diagnosticserver.h
+ diagnosticsprotocol.h
dllimportcallback.h
eeconfig.h
eecontract.h
@@ -439,10 +445,13 @@ set(VM_HEADERS_WKS
eventpipeeventinstance.h
eventpipeeventsource.h
eventpipefile.h
+ eventpipeinternal.h
eventpipejsonfile.h
eventpipemetadatagenerator.h
+ eventpipeprotocolhelper.h
eventpipeprovider.h
eventpipesession.h
+ eventpipesessionprovider.h
eventstore.hpp
fastserializer.h
fcall.h
diff --git a/src/vm/appdomain.cpp b/src/vm/appdomain.cpp
index 4eb716164c..bcffb3b8aa 100644
--- a/src/vm/appdomain.cpp
+++ b/src/vm/appdomain.cpp
@@ -2485,6 +2485,11 @@ void SystemDomain::LoadBaseSystemClasses()
// Load String
g_pStringClass = MscorlibBinder::LoadPrimitiveType(ELEMENT_TYPE_STRING);
+#ifdef FEATURE_UTF8STRING
+ // Load Utf8String
+ g_pUtf8StringClass = MscorlibBinder::GetClass(CLASS__UTF8_STRING);
+#endif // FEATURE_UTF8STRING
+
// Used by Buffer::BlockCopy
g_pByteArrayMT = ClassLoader::LoadArrayTypeThrowing(
TypeHandle(MscorlibBinder::GetElementType(ELEMENT_TYPE_U1))).AsArray()->GetMethodTable();
@@ -4218,8 +4223,8 @@ static const char *fileLoadLevelName[] =
"LOADLIBRARY", // FILE_LOAD_LOADLIBRARY
"POST_LOADLIBRARY", // FILE_LOAD_POST_LOADLIBRARY
"EAGER_FIXUPS", // FILE_LOAD_EAGER_FIXUPS
- "VTABLE FIXUPS", // FILE_LOAD_VTABLE_FIXUPS
"DELIVER_EVENTS", // FILE_LOAD_DELIVER_EVENTS
+ "VTABLE FIXUPS", // FILE_LOAD_VTABLE_FIXUPS
"LOADED", // FILE_LOADED
"VERIFY_EXECUTION", // FILE_LOAD_VERIFY_EXECUTION
"ACTIVE", // FILE_ACTIVE
diff --git a/src/vm/assemblynative.cpp b/src/vm/assemblynative.cpp
index 30ee95b35f..ed2ce660e7 100644
--- a/src/vm/assemblynative.cpp
+++ b/src/vm/assemblynative.cpp
@@ -363,6 +363,42 @@ void QCALLTYPE AssemblyNative::LoadFromStream(INT_PTR ptrNativeAssemblyLoadConte
END_QCALL;
}
+#ifndef FEATURE_PAL
+/*static */
+void QCALLTYPE AssemblyNative::LoadFromInMemoryModule(INT_PTR ptrNativeAssemblyLoadContext, INT_PTR hModule, QCall::ObjectHandleOnStack retLoadedAssembly)
+{
+ QCALL_CONTRACT;
+
+ BEGIN_QCALL;
+
+ // Ensure that the invariants are in place
+ _ASSERTE(ptrNativeAssemblyLoadContext != NULL);
+ _ASSERTE(hModule != NULL);
+
+ PEImageHolder pILImage(PEImage::LoadImage((HMODULE)hModule));
+
+ // Need to verify that this is a valid CLR assembly.
+ if (!pILImage->HasCorHeader())
+ ThrowHR(COR_E_BADIMAGEFORMAT, BFA_BAD_IL);
+
+ // Get the binder context in which the assembly will be loaded
+ ICLRPrivBinder *pBinderContext = reinterpret_cast<ICLRPrivBinder*>(ptrNativeAssemblyLoadContext);
+
+ // Pass the in memory module as IL in an attempt to bind and load it
+ Assembly* pLoadedAssembly = AssemblyNative::LoadFromPEImage(pBinderContext, pILImage, NULL);
+ {
+ GCX_COOP();
+ retLoadedAssembly.Set(pLoadedAssembly->GetExposedObject());
+ }
+
+ LOG((LF_CLASSLOADER,
+ LL_INFO100,
+ "\tLoaded assembly from pre-loaded native module\n"));
+
+ END_QCALL;
+}
+#endif
+
void QCALLTYPE AssemblyNative::GetLocation(QCall::AssemblyHandle pAssembly, QCall::StringHandleOnStack retString)
{
QCALL_CONTRACT;
diff --git a/src/vm/assemblynative.hpp b/src/vm/assemblynative.hpp
index d506b6e407..13db261359 100644
--- a/src/vm/assemblynative.hpp
+++ b/src/vm/assemblynative.hpp
@@ -120,6 +120,9 @@ public:
static void QCALLTYPE LoadFromPath(INT_PTR ptrNativeAssemblyLoadContext, LPCWSTR pwzILPath, LPCWSTR pwzNIPath, QCall::ObjectHandleOnStack retLoadedAssembly);
static INT_PTR QCALLTYPE InternalLoadUnmanagedDllFromPath(LPCWSTR unmanagedLibraryPath);
static void QCALLTYPE LoadFromStream(INT_PTR ptrNativeAssemblyLoadContext, INT_PTR ptrAssemblyArray, INT32 cbAssemblyArrayLength, INT_PTR ptrSymbolArray, INT32 cbSymbolArrayLength, QCall::ObjectHandleOnStack retLoadedAssembly);
+#ifndef FEATURE_PAL
+ static void QCALLTYPE LoadFromInMemoryModule(INT_PTR ptrNativeAssemblyLoadContext, INT_PTR hModule, QCall::ObjectHandleOnStack retLoadedAssembly);
+#endif
static Assembly* LoadFromPEImage(ICLRPrivBinder* pBinderContext, PEImage *pILImage, PEImage *pNIImage);
static INT_PTR QCALLTYPE GetLoadContextForAssembly(QCall::AssemblyHandle pAssembly);
diff --git a/src/vm/ceeload.cpp b/src/vm/ceeload.cpp
index c0fad3c595..b9cf4a7900 100644
--- a/src/vm/ceeload.cpp
+++ b/src/vm/ceeload.cpp
@@ -191,6 +191,35 @@ BOOL Module::SetTransientFlagInterlocked(DWORD dwFlag)
}
#if PROFILING_SUPPORTED
+void Module::UpdateNewlyAddedTypes()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ }
+ CONTRACTL_END
+
+ DWORD countTypesAfterProfilerUpdate = GetMDImport()->GetCountWithTokenKind(mdtTypeDef);
+ DWORD countExportedTypesAfterProfilerUpdate = GetMDImport()->GetCountWithTokenKind(mdtExportedType);
+
+ // typeDefs rids 0 and 1 aren't included in the count, thus X typeDefs before means rid X+1 was valid and our incremental addition should start at X+2
+ for (DWORD typeDefRid = m_dwTypeCount + 2; typeDefRid < countTypesAfterProfilerUpdate + 2; typeDefRid++)
+ {
+ GetAssembly()->AddType(this, TokenFromRid(typeDefRid, mdtTypeDef));
+ }
+
+ // exportedType rid 0 isn't included in the count, thus X exportedTypes before means rid X was valid and our incremental addition should start at X+1
+ for (DWORD exportedTypeDef = m_dwExportedTypeCount + 1; exportedTypeDef < countExportedTypesAfterProfilerUpdate + 1; exportedTypeDef++)
+ {
+ GetAssembly()->AddExportedType(TokenFromRid(exportedTypeDef, mdtExportedType));
+ }
+
+ m_dwTypeCount = countTypesAfterProfilerUpdate;
+ m_dwExportedTypeCount = countExportedTypesAfterProfilerUpdate;
+}
+
void Module::NotifyProfilerLoadFinished(HRESULT hr)
{
CONTRACTL
@@ -208,12 +237,10 @@ void Module::NotifyProfilerLoadFinished(HRESULT hr)
if (SetTransientFlagInterlocked(IS_PROFILER_NOTIFIED))
{
// Record how many types are already present
- DWORD countTypesOrig = 0;
- DWORD countExportedTypesOrig = 0;
if (!IsResource())
{
- countTypesOrig = GetMDImport()->GetCountWithTokenKind(mdtTypeDef);
- countExportedTypesOrig = GetMDImport()->GetCountWithTokenKind(mdtExportedType);
+ m_dwTypeCount = GetMDImport()->GetCountWithTokenKind(mdtTypeDef);
+ m_dwExportedTypeCount = GetMDImport()->GetCountWithTokenKind(mdtExportedType);
}
// Notify the profiler, this may cause metadata to be updated
@@ -236,18 +263,7 @@ void Module::NotifyProfilerLoadFinished(HRESULT hr)
// assembly
if (!IsResource())
{
- DWORD countTypesAfterProfilerUpdate = GetMDImport()->GetCountWithTokenKind(mdtTypeDef);
- DWORD countExportedTypesAfterProfilerUpdate = GetMDImport()->GetCountWithTokenKind(mdtExportedType);
- // typeDefs rids 0 and 1 aren't included in the count, thus X typeDefs before means rid X+1 was valid and our incremental addition should start at X+2
- for (DWORD typeDefRid = countTypesOrig + 2; typeDefRid < countTypesAfterProfilerUpdate + 2; typeDefRid++)
- {
- GetAssembly()->AddType(this, TokenFromRid(typeDefRid, mdtTypeDef));
- }
- // exportedType rid 0 isn't included in the count, thus X exportedTypes before means rid X was valid and our incremental addition should start at X+1
- for (DWORD exportedTypeDef = countExportedTypesOrig + 1; exportedTypeDef < countExportedTypesAfterProfilerUpdate + 1; exportedTypeDef++)
- {
- GetAssembly()->AddExportedType(TokenFromRid(exportedTypeDef, mdtExportedType));
- }
+ UpdateNewlyAddedTypes();
}
{
@@ -585,6 +601,11 @@ void Module::Initialize(AllocMemTracker *pamTracker, LPCWSTR szName)
m_ModuleID = NULL;
m_ModuleIndex.m_dwIndex = (SIZE_T)-1;
+ // These will be initialized in NotifyProfilerLoadFinished, set them to
+ // a safe initial value now.
+ m_dwTypeCount = 0;
+ m_dwExportedTypeCount = 0;
+
// Prepare statics that are known at module load time
AllocateStatics(pamTracker);
@@ -1187,7 +1208,7 @@ void Module::ApplyMetaData()
CONTRACTL
{
THROWS;
- GC_NOTRIGGER;
+ GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
@@ -1197,6 +1218,13 @@ void Module::ApplyMetaData()
HRESULT hr = S_OK;
ULONG ulCount;
+#if PROFILING_SUPPORTED
+ if (!IsResource())
+ {
+ UpdateNewlyAddedTypes();
+ }
+#endif // PROFILING_SUPPORTED
+
// Ensure for TypeRef
ulCount = GetMDImport()->GetCountWithTokenKind(mdtTypeRef) + 1;
EnsureTypeRefCanBeStored(TokenFromRid(ulCount, mdtTypeRef));
@@ -1204,6 +1232,10 @@ void Module::ApplyMetaData()
// Ensure for AssemblyRef
ulCount = GetMDImport()->GetCountWithTokenKind(mdtAssemblyRef) + 1;
EnsureAssemblyRefCanBeStored(TokenFromRid(ulCount, mdtAssemblyRef));
+
+ // Ensure for MethodDef
+ ulCount = GetMDImport()->GetCountWithTokenKind(mdtMethodDef) + 1;
+ EnsureMethodDefCanBeStored(TokenFromRid(ulCount, mdtMethodDef));
}
//
@@ -6450,6 +6482,59 @@ void Module::NotifyDebuggerUnload(AppDomain *pDomain)
}
#if !defined(CROSSGEN_COMPILE)
+using GetTokenForVTableEntry_t = mdToken(STDMETHODCALLTYPE*)(HMODULE module, BYTE**ppVTEntry);
+
+static HMODULE GetIJWHostForModule(Module* module)
+{
+#if !defined(FEATURE_PAL)
+ PEDecoder* pe = module->GetFile()->GetLoadedIL();
+
+ BYTE* baseAddress = (BYTE*)module->GetFile()->GetIJWBase();
+
+ IMAGE_IMPORT_DESCRIPTOR* importDescriptor = (IMAGE_IMPORT_DESCRIPTOR*)pe->GetDirectoryData(pe->GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT));
+
+ for(; importDescriptor->Characteristics != 0; importDescriptor++)
+ {
+ IMAGE_THUNK_DATA* importNameTable = (IMAGE_THUNK_DATA*)pe->GetRvaData(importDescriptor->OriginalFirstThunk);
+
+ IMAGE_THUNK_DATA* importAddressTable = (IMAGE_THUNK_DATA*)pe->GetRvaData(importDescriptor->FirstThunk);
+
+ for (int thunkIndex = 0; importNameTable[thunkIndex].u1.AddressOfData != 0; thunkIndex++)
+ {
+ // The most significant bit will be set if the entry points to an ordinal.
+ if ((importNameTable[thunkIndex].u1.Ordinal & (1LL << (sizeof(importNameTable[thunkIndex].u1.Ordinal) * CHAR_BIT - 1))) == 0)
+ {
+ IMAGE_IMPORT_BY_NAME* nameImport = (IMAGE_IMPORT_BY_NAME*)(baseAddress + importNameTable[thunkIndex].u1.AddressOfData);
+ if (strcmp("_CorDllMain", nameImport->Name) == 0)
+ {
+ HMODULE ijwHost;
+
+ if (WszGetModuleHandleEx(
+ GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ (LPCWSTR)importAddressTable[thunkIndex].u1.Function,
+ &ijwHost))
+ {
+ return ijwHost;
+ }
+
+ }
+ }
+ }
+ }
+#endif
+ return nullptr;
+}
+
+static GetTokenForVTableEntry_t GetTokenGetterFromHostModule(HMODULE ijwHost)
+{
+ if (ijwHost != nullptr)
+ {
+ return (GetTokenForVTableEntry_t)GetProcAddress(ijwHost, "GetTokenForVTableEntry");
+ }
+
+ return nullptr;
+}
+
//=================================================================================
mdToken GetTokenForVTableEntry(HINSTANCE hInst, BYTE **ppVTEntry)
{
@@ -6472,7 +6557,6 @@ void SetTargetForVTableEntry(HINSTANCE hInst, BYTE **ppVTEntry, BYTE *pTarget)
DWORD oldProtect;
if (!ClrVirtualProtect(ppVTEntry, sizeof(BYTE*), PAGE_READWRITE, &oldProtect))
{
-
// This is very bad. We are not going to be able to update header.
_ASSERTE(!"SetTargetForVTableEntry(): VirtualProtect() changing IJW thunk vtable to R/W failed.\n");
ThrowLastError();
@@ -6516,6 +6600,18 @@ void Module::FixupVTables()
return;
}
+ // Try getting a callback to the IJW host if it is loaded.
+ // The IJW host substitutes in special shims in the vtfixup table
+ // so if it is loaded, we need to query it for the tokens that were in the slots.
+ // If it is not loaded, then we know that the vtfixup table entries are tokens,
+ // so we can resolve them ourselves.
+ GetTokenForVTableEntry_t GetTokenForVTableEntryCallback = GetTokenGetterFromHostModule(GetIJWHostForModule(this));
+
+ if (GetTokenForVTableEntryCallback == nullptr)
+ {
+ GetTokenForVTableEntryCallback = GetTokenForVTableEntry;
+ }
+
HINSTANCE hInstThis = GetFile()->GetIJWBase();
// <REVISIT_TODO>@todo: workaround!</REVISIT_TODO>
@@ -6616,7 +6712,7 @@ void Module::FixupVTables()
{
if (pData->IsMethodFixedUp(iFixup, iMethod))
continue;
- mdToken mdTok = GetTokenForVTableEntry(hInstThis, (BYTE **)(pPointers + iMethod));
+ mdToken mdTok = GetTokenForVTableEntryCallback(hInstThis, (BYTE**)(pPointers + iMethod));
CONSISTENCY_CHECK(mdTok != mdTokenNil);
rgMethodsToLoad[iCurMethod++].token = mdTok;
}
diff --git a/src/vm/ceeload.h b/src/vm/ceeload.h
index c3a9609ba0..01d5ba880a 100644
--- a/src/vm/ceeload.h
+++ b/src/vm/ceeload.h
@@ -1627,6 +1627,11 @@ private:
BOOL m_nativeImageProfiling;
CORCOMPILE_METHOD_PROFILE_LIST *m_methodProfileList;
+#if PROFILING_SUPPORTED_DATA
+ DWORD m_dwTypeCount;
+ DWORD m_dwExportedTypeCount;
+#endif // PROFILING_SUPPORTED_DATA
+
#if defined(FEATURE_COMINTEROP)
public:
@@ -2526,6 +2531,8 @@ public:
DEBUGGER_INFO_SHIFT_PRIV);
}
+ void UpdateNewlyAddedTypes();
+
#ifdef PROFILING_SUPPORTED
BOOL IsProfilerNotified() {LIMITED_METHOD_CONTRACT; return (m_dwTransientFlags & IS_PROFILER_NOTIFIED) != 0; }
void NotifyProfilerLoadFinished(HRESULT hr);
diff --git a/src/vm/ceemain.cpp b/src/vm/ceemain.cpp
index e5267cf131..1b85649d67 100644
--- a/src/vm/ceemain.cpp
+++ b/src/vm/ceemain.cpp
@@ -215,6 +215,7 @@
#include "perfmap.h"
#endif
+#include "diagnosticserver.h"
#include "eventpipe.h"
#ifndef FEATURE_PAL
@@ -667,6 +668,7 @@ void EEStartupHelper(COINITIEE fFlags)
#ifdef FEATURE_PERFTRACING
// Initialize the event pipe.
+ DiagnosticServer::Initialize();
EventPipe::Initialize();
#endif // FEATURE_PERFTRACING
@@ -1462,6 +1464,7 @@ void STDMETHODCALLTYPE EEShutDownHelper(BOOL fIsDllUnloading)
#ifdef FEATURE_PERFTRACING
// Shutdown the event pipe.
EventPipe::Shutdown();
+ DiagnosticServer::Shutdown();
#endif // FEATURE_PERFTRACING
#if defined(FEATURE_COMINTEROP)
diff --git a/src/vm/classnames.h b/src/vm/classnames.h
index cb71df362a..f45311f0de 100644
--- a/src/vm/classnames.h
+++ b/src/vm/classnames.h
@@ -139,6 +139,10 @@
#define g_ThreadClassName "System.Threading.Thread"
#define g_TypeClassName "System.Type"
+#ifdef FEATURE_UTF8STRING
+#define g_Utf8StringName "Utf8String"
+#endif // FEATURE_UTF8STRING
+
#define g_VariantClassName "System.Variant"
#define g_GuidClassName "System.Guid"
diff --git a/src/vm/clsload.cpp b/src/vm/clsload.cpp
index 4ffbb3fced..323e29ba60 100644
--- a/src/vm/clsload.cpp
+++ b/src/vm/clsload.cpp
@@ -840,7 +840,8 @@ void ClassLoader::GetClassValue(NameHandleTable nhTable,
continue;
#ifdef FEATURE_READYTORUN
- if (nhTable == nhCaseSensitive && pCurrentClsModule->IsReadyToRun() && pCurrentClsModule->GetReadyToRunInfo()->HasHashtableOfTypes())
+ if (nhTable == nhCaseSensitive && pCurrentClsModule->IsReadyToRun() && pCurrentClsModule->GetReadyToRunInfo()->HasHashtableOfTypes() &&
+ pCurrentClsModule->GetAvailableClassHash() == NULL)
{
// For R2R modules, we only search the hashtable of token types stored in the module's image, and don't fallback
// to searching m_pAvailableClasses or m_pAvailableClassesCaseIns (in fact, we don't even allocate them for R2R modules).
@@ -1618,7 +1619,7 @@ BOOL ClassLoader::FindClassModuleThrowing(
EEClassHashEntry_t * pBucket = foundEntry.GetClassHashBasedEntryValue();
- if (pBucket == NULL && needsToBuildHashtable)
+ if (pBucket == NULL)
{
AvailableClasses_LockHolder lh(this);
@@ -1628,7 +1629,7 @@ BOOL ClassLoader::FindClassModuleThrowing(
pBucket = foundEntry.GetClassHashBasedEntryValue();
#ifndef DACCESS_COMPILE
- if ((pBucket == NULL) && (m_cUnhashedModules > 0))
+ if (needsToBuildHashtable && (pBucket == NULL) && (m_cUnhashedModules > 0))
{
_ASSERT(needsToBuildHashtable);
@@ -1650,6 +1651,7 @@ BOOL ClassLoader::FindClassModuleThrowing(
#endif
}
+ // Same check as above, but this time we've checked with the lock so the table will be populated
if (pBucket == NULL)
{
#if defined(_DEBUG_IMPL) && !defined(DACCESS_COMPILE)
@@ -4184,6 +4186,15 @@ VOID ClassLoader::AddAvailableClassDontHaveLock(Module *pModule,
#endif
CrstHolder ch(&m_AvailableClassLock);
+
+ // R2R pre-computes an export table and tries to avoid populating a class hash at runtime. However the profiler can
+ // still add new types on the fly by calling here. If that occurs we fallback to the slower path of creating the
+ // in memory hashtable as usual.
+ if (!pModule->IsResource() && pModule->GetAvailableClassHash() == NULL)
+ {
+ LazyPopulateCaseSensitiveHashTables();
+ }
+
AddAvailableClassHaveLock(
pModule,
classdef,
@@ -4380,6 +4391,15 @@ VOID ClassLoader::AddExportedTypeDontHaveLock(Module *pManifestModule,
CONTRACTL_END
CrstHolder ch(&m_AvailableClassLock);
+
+ // R2R pre-computes an export table and tries to avoid populating a class hash at runtime. However the profiler can
+ // still add new types on the fly by calling here. If that occurs we fallback to the slower path of creating the
+ // in memory hashtable as usual.
+ if (!pManifestModule->IsResource() && pManifestModule->GetAvailableClassHash() == NULL)
+ {
+ LazyPopulateCaseSensitiveHashTables();
+ }
+
AddExportedTypeHaveLock(
pManifestModule,
cl,
diff --git a/src/vm/comdelegate.cpp b/src/vm/comdelegate.cpp
index 9a131fa126..42f457ff3a 100644
--- a/src/vm/comdelegate.cpp
+++ b/src/vm/comdelegate.cpp
@@ -1626,9 +1626,35 @@ FCIMPL3(PCODE, COMDelegate::AdjustTarget, Object* refThisUNSAFE, Object* targetU
// close delegates
MethodTable* pMTTarg = target->GetMethodTable();
MethodTable* pMTMeth = pMeth->GetMethodTable();
+
+ BOOL isComObject = false;
+
+#ifdef FEATURE_COMINTEROP
+ isComObject = pMTTarg->IsComObjectType();
+#endif // FEATURE_COMINTEROP
MethodDesc *pCorrectedMethod = pMeth;
+ if (pMTMeth != pMTTarg)
+ {
+ //They cast to an interface before creating the delegate, so we now need
+ //to figure out where this actually lives before we continue.
+ //<TODO>@perf: Grovelling with a signature is really slow. Speed this up.</TODO>
+ if (pCorrectedMethod->IsInterface())
+ {
+ // No need to resolve the interface based method desc to a class based
+ // one for COM objects because we invoke directly thru the interface MT.
+ if (!isComObject)
+ {
+ // <TODO>it looks like we need to pass an ownerType in here.
+ // Why can we take a delegate to an interface method anyway? </TODO>
+ //
+ pCorrectedMethod = pMTTarg->FindDispatchSlotForInterfaceMD(pCorrectedMethod, TRUE /* throwOnConflict */).GetMethodDesc();
+ _ASSERTE(pCorrectedMethod != NULL);
+ }
+ }
+ }
+
// Use the Unboxing stub for value class methods, since the value
// class is constructed using the boxed instance.
if (pCorrectedMethod->GetMethodTable()->IsValueType() && !pCorrectedMethod->IsUnboxingStub())
@@ -1772,6 +1798,53 @@ FCIMPL3(void, COMDelegate::DelegateConstruct, Object* refThisUNSAFE, Object* tar
{
if (pMTTarg)
{
+ // We can skip the demand if SuppressUnmanagedCodePermission is present on the class,
+ // or in the case where we are setting up a delegate for a COM event sink
+ // we can skip the check if the source interface is defined in fully trusted code
+ // we can skip the check if the source interface is a disp-only interface
+ BOOL isComObject = false;
+#ifdef FEATURE_COMINTEROP
+ isComObject = pMTTarg->IsComObjectType();
+#endif // FEATURE_COMINTEROP
+
+ if (pMTMeth != pMTTarg)
+ {
+ // They cast to an interface before creating the delegate, so we now need
+ // to figure out where this actually lives before we continue.
+ // <TODO>@perf: We whould never be using this path to invoke on an interface -
+ // that should always be resolved when we are creating the delegate </TODO>
+ if (pMeth->IsInterface())
+ {
+ // No need to resolve the interface based method desc to a class based
+ // one for COM objects because we invoke directly thru the interface MT.
+ if (!isComObject)
+ {
+ // <TODO>it looks like we need to pass an ownerType in here.
+ // Why can we take a delegate to an interface method anyway? </TODO>
+ //
+ MethodDesc * pDispatchSlotMD = pMTTarg->FindDispatchSlotForInterfaceMD(pMeth, TRUE /* throwOnConflict */).GetMethodDesc();
+ if (pDispatchSlotMD == NULL)
+ {
+ COMPlusThrow(kArgumentException, W("Arg_DlgtTargMeth"));
+ }
+
+ if (pMeth->HasMethodInstantiation())
+ {
+ pMeth = MethodDesc::FindOrCreateAssociatedMethodDesc(
+ pDispatchSlotMD,
+ pMTTarg,
+ (!pDispatchSlotMD->IsStatic() && pMTTarg->IsValueType()),
+ pMeth->GetMethodInstantiation(),
+ FALSE /* allowInstParam */);
+ }
+ else
+ {
+ pMeth = pDispatchSlotMD;
+ }
+ }
+ }
+ }
+
g_IBCLogger.LogMethodTableAccess(pMTTarg);
// Use the Unboxing stub for value class methods, since the value
@@ -3157,9 +3230,10 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT
#endif
// under the conditions below the delegate ctor needs to perform some heavy operation
- // to get the unboxing stub
+ // to either resolve the interface call to the real target or to get the unboxing stub (or both)
BOOL needsRuntimeInfo = !pTargetMethod->IsStatic() &&
- pTargetMethod->GetMethodTable()->IsValueType() && !pTargetMethod->IsUnboxingStub();
+ (pTargetMethod->IsInterface() ||
+ (pTargetMethod->GetMethodTable()->IsValueType() && !pTargetMethod->IsUnboxingStub()));
if (needsRuntimeInfo)
pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_RT_CLOSED);
diff --git a/src/vm/common.h b/src/vm/common.h
index 9050ae7c63..61ba2a7514 100644
--- a/src/vm/common.h
+++ b/src/vm/common.h
@@ -75,7 +75,6 @@
#include <stdlib.h>
#include <wchar.h>
#include <objbase.h>
-#include <stddef.h>
#include <float.h>
#include <math.h>
#include <time.h>
@@ -168,6 +167,9 @@ typedef DPTR(class ReJitManager) PTR_ReJitManager;
typedef DPTR(struct ReJitInfo) PTR_ReJitInfo;
typedef DPTR(struct SharedReJitInfo) PTR_SharedReJitInfo;
typedef DPTR(class StringObject) PTR_StringObject;
+#ifdef FEATURE_UTF8STRING
+typedef DPTR(class Utf8StringObject) PTR_Utf8StringObject;
+#endif // FEATURE_UTF8STRING
typedef DPTR(class TypeHandle) PTR_TypeHandle;
typedef VPTR(class VirtualCallStubManager) PTR_VirtualCallStubManager;
typedef VPTR(class VirtualCallStubManagerManager) PTR_VirtualCallStubManagerManager;
diff --git a/src/vm/diagnosticserver.cpp b/src/vm/diagnosticserver.cpp
new file mode 100644
index 0000000000..3a700383ac
--- /dev/null
+++ b/src/vm/diagnosticserver.cpp
@@ -0,0 +1,181 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "common.h"
+#include "diagnosticserver.h"
+#include "eventpipeprotocolhelper.h"
+
+#ifdef FEATURE_PAL
+#include "pal.h"
+#endif // FEATURE_PAL
+
+#ifdef FEATURE_PERFTRACING
+
+IpcStream::DiagnosticsIpc *DiagnosticServer::s_pIpc = nullptr;
+
+static DWORD WINAPI DiagnosticsServerThread(LPVOID lpThreadParameter)
+{
+ CONTRACTL
+ {
+ // TODO: Maybe this should not throw.
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(lpThreadParameter != nullptr);
+ }
+ CONTRACTL_END;
+
+ auto pIpc = reinterpret_cast<IpcStream::DiagnosticsIpc *>(lpThreadParameter);
+ if (pIpc == nullptr)
+ {
+ STRESS_LOG0(LF_DIAGNOSTICS_PORT, LL_ERROR, "Diagnostics IPC listener was undefined\n");
+ return 1;
+ }
+
+ ErrorCallback LoggingCallback = [](const char *szMessage, uint32_t code) {
+ STRESS_LOG2(LF_DIAGNOSTICS_PORT, LL_WARNING, "warning (%d): %s.\n", code, szMessage);
+ };
+
+ while (true)
+ {
+ // FIXME: Ideally this would be something like a std::shared_ptr
+ IpcStream *pStream = pIpc->Accept(LoggingCallback);
+ if (pStream == nullptr)
+ continue;
+
+ // TODO: Read operation should happen in a loop.
+ uint32_t nNumberOfBytesRead = 0;
+ MessageHeader header;
+ bool fSuccess = pStream->Read(&header, sizeof(header), nNumberOfBytesRead);
+ if (!fSuccess || nNumberOfBytesRead != sizeof(header))
+ {
+ delete pStream;
+ continue;
+ }
+
+ // TODO: Dispatch thread worker.
+ switch (header.RequestType)
+ {
+ case DiagnosticMessageType::EnableEventPipe:
+ EventPipeProtocolHelper::EnableFileTracingEventHandler(pStream);
+ break;
+
+ case DiagnosticMessageType::DisableEventPipe:
+ EventPipeProtocolHelper::DisableTracingEventHandler(pStream);
+ break;
+
+ default:
+ LOG((LF_DIAGNOSTICS_PORT, LL_WARNING, "Received unknow request type (%d)\n", header.RequestType));
+ break;
+ }
+ }
+
+ return 0;
+}
+
+bool DiagnosticServer::Initialize()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ bool fSuccess = false;
+
+ EX_TRY
+ {
+ auto ErrorCallback = [](const char *szMessage, uint32_t code) {
+ STRESS_LOG2(
+ LF_DIAGNOSTICS_PORT, // facility
+ LL_ERROR, // level
+ "Failed to create diagnostic IPC: error (%d): %s.\n", // msg
+ code, // data1
+ szMessage); // data2
+ };
+
+ // TODO: Should we handle/assert that (s_pIpc == nullptr)?
+ s_pIpc = IpcStream::DiagnosticsIpc::Create(
+ "dotnetcore-diagnostic", ErrorCallback);
+
+ if (s_pIpc != nullptr)
+ {
+ DWORD dwThreadId = 0;
+ HANDLE hThread = ::CreateThread( // TODO: Is it correct to have this "lower" level call here?
+ nullptr, // no security attribute
+ 0, // default stack size
+ DiagnosticsServerThread, // thread proc
+ (LPVOID)s_pIpc, // thread parameter
+ 0, // not suspended
+ &dwThreadId); // returns thread ID
+
+ if (hThread == nullptr)
+ {
+ // Failed to create IPC thread.
+ STRESS_LOG1(
+ LF_DIAGNOSTICS_PORT, // facility
+ LL_ERROR, // level
+ "Failed to create diagnostic server thread (%d).\n", // msg
+ ::GetLastError()); // data1
+ }
+ else
+ {
+ // FIXME: Maybe hold on to the thread to abort/cleanup at exit?
+ ::CloseHandle(hThread);
+
+ // TODO: Add error handling?
+ fSuccess = true;
+ }
+ }
+ }
+ EX_CATCH
+ {
+ // TODO: Should we log anything here?
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return fSuccess;
+}
+
+bool DiagnosticServer::Shutdown()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ bool fSuccess = false;
+
+ EX_TRY
+ {
+ if (s_pIpc != nullptr)
+ {
+ auto ErrorCallback = [](const char *szMessage, uint32_t code) {
+ STRESS_LOG2(
+ LF_DIAGNOSTICS_PORT, // facility
+ LL_ERROR, // level
+ "Failed to unlink diagnostic IPC: error (%d): %s.\n", // msg
+ code, // data1
+ szMessage); // data2
+ };
+ s_pIpc->Unlink(ErrorCallback);
+ }
+ fSuccess = true;
+ }
+ EX_CATCH
+ {
+ fSuccess = false;
+ // TODO: Should we log anything here?
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return fSuccess;
+}
+
+#endif // FEATURE_PERFTRACING
diff --git a/src/vm/diagnosticserver.h b/src/vm/diagnosticserver.h
new file mode 100644
index 0000000000..51f32ae09f
--- /dev/null
+++ b/src/vm/diagnosticserver.h
@@ -0,0 +1,62 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __DIAGNOSTIC_SERVER_H__
+#define __DIAGNOSTIC_SERVER_H__
+
+#ifdef FEATURE_PERFTRACING // This macro should change to something more generic than performance.
+
+#include <stdint.h>
+#include "diagnosticsipc.h"
+
+//! TODO: Temp class.
+enum class DiagnosticMessageType : uint32_t
+{
+ ///////////////////////////////////////////////////////////////////////////
+ // Debug = 0
+
+ ///////////////////////////////////////////////////////////////////////////
+ // EventPipe
+ EnableEventPipe = 1024,
+ DisableEventPipe,
+
+ // TODO: Define what else is available on the out-of-proc interface?
+ // GetSessionInfo,
+ // CreateProvider,
+ // DefineEvent,
+ // GetProvider,
+ // DeleteProvider,
+ // EventActivityIdControl,
+ // WriteEvent,
+ // WriteEventData,
+ // GetNextEvent,
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Profiler = 2048
+};
+
+//! TODO: Temp class.
+struct MessageHeader
+{
+ DiagnosticMessageType RequestType;
+ uint32_t Pid;
+};
+
+//! Defines an implementation of a IPC handler that dispatches messages to the runtime.
+class DiagnosticServer final
+{
+public:
+ //! Initialize the event pipe (Creates the EventPipe IPC server).
+ static bool Initialize();
+
+ //! Shutdown the event pipe.
+ static bool Shutdown();
+
+private:
+ static IpcStream::DiagnosticsIpc *s_pIpc;
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __DIAGNOSTIC_SERVER_H__
diff --git a/src/vm/diagnosticsprotocol.h b/src/vm/diagnosticsprotocol.h
new file mode 100644
index 0000000000..765977f02a
--- /dev/null
+++ b/src/vm/diagnosticsprotocol.h
@@ -0,0 +1,54 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __DIAGNOSTICS_PROTOCOL_H__
+#define __DIAGNOSTICS_PROTOCOL_H__
+
+#ifdef FEATURE_PERFTRACING
+
+template <typename T>
+bool TryParse(uint8_t *&bufferCursor, uint32_t &bufferLen, T &result)
+{
+ static_assert(
+ std::is_integral<T>::value || std::is_same<T, float>::value || std::is_same<T, double>::value,
+ "Can only be instantiated with integral and floating point types.");
+
+ if (bufferLen < sizeof(T))
+ return false;
+ result = *(reinterpret_cast<T *>(bufferCursor));
+ bufferCursor += sizeof(T);
+ bufferLen -= sizeof(T);
+ return true;
+}
+
+template <typename T>
+bool TryParseString(uint8_t *&bufferCursor, uint32_t &bufferLen, const T *&result)
+{
+ static_assert(
+ std::is_same<T, char>::value || std::is_same<T, wchar_t>::value,
+ "Can only be instantiated with char and wchar_t types.");
+
+ uint32_t stringLen = 0;
+ if (!TryParse(bufferCursor, bufferLen, stringLen))
+ return false;
+ if (stringLen == 0)
+ {
+ result = nullptr;
+ return true;
+ }
+ if (stringLen > (bufferLen / sizeof(T)))
+ return false;
+ if ((reinterpret_cast<const T *>(bufferCursor))[stringLen - 1] != 0)
+ return false;
+ result = reinterpret_cast<const T *>(bufferCursor);
+
+ const uint32_t TotalStringLength = stringLen * sizeof(T);
+ bufferCursor += TotalStringLength;
+ bufferLen -= TotalStringLength;
+ return true;
+}
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __DIAGNOSTICS_PROTOCOL_H__
diff --git a/src/vm/dllimport.cpp b/src/vm/dllimport.cpp
index 6fb16c51f6..b200a1bccb 100644
--- a/src/vm/dllimport.cpp
+++ b/src/vm/dllimport.cpp
@@ -401,7 +401,7 @@ public:
pcsUnmarshal->EmitLDLOCA(dwFactoryRetValLocalNum);
}
- pcsUnmarshal->EmitCALL(METHOD__MARSHAL__INITIALIZE_WRAPPER_FOR_WINRT, 2, 0);
+ pcsUnmarshal->EmitCALL(METHOD__WINDOWSRUNTIMEMARSHAL__INITIALIZE_WRAPPER, 2, 0);
/*
* CLEANUP
@@ -650,7 +650,7 @@ public:
BinderMethodID getHRForException;
if (SF_IsWinRTStub(m_dwStubFlags))
{
- getHRForException = METHOD__MARSHAL__GET_HR_FOR_EXCEPTION_WINRT;
+ getHRForException = METHOD__WINDOWSRUNTIMEMARSHAL__GET_HR_FOR_EXCEPTION;
}
else
{
@@ -4139,6 +4139,7 @@ static void CreateNDirectStubWorker(StubState* pss,
COMPlusThrow(kMarshalDirectiveException, IDS_EE_NDIRECT_BADNATL_THISCALL);
}
+ fHasCopyCtorArgs = info.GetMarshalType() == MarshalInfo::MARSHAL_TYPE_BLITTABLEVALUECLASSWITHCOPYCTOR ? TRUE : FALSE;
argidx++;
}
diff --git a/src/vm/domainfile.cpp b/src/vm/domainfile.cpp
index 0fd9c811d5..5c89767d1a 100644
--- a/src/vm/domainfile.cpp
+++ b/src/vm/domainfile.cpp
@@ -555,14 +555,14 @@ BOOL DomainFile::DoIncrementalLoad(FileLoadLevel level)
EagerFixups();
break;
- case FILE_LOAD_VTABLE_FIXUPS:
- VtableFixups();
- break;
-
case FILE_LOAD_DELIVER_EVENTS:
DeliverSyncEvents();
break;
+ case FILE_LOAD_VTABLE_FIXUPS:
+ VtableFixups();
+ break;
+
case FILE_LOADED:
FinishLoad();
break;
diff --git a/src/vm/domainfile.h b/src/vm/domainfile.h
index d6917b1fd5..ddc0384c58 100644
--- a/src/vm/domainfile.h
+++ b/src/vm/domainfile.h
@@ -45,8 +45,8 @@ enum FileLoadLevel
FILE_LOAD_LOADLIBRARY,
FILE_LOAD_POST_LOADLIBRARY,
FILE_LOAD_EAGER_FIXUPS,
- FILE_LOAD_VTABLE_FIXUPS,
FILE_LOAD_DELIVER_EVENTS,
+ FILE_LOAD_VTABLE_FIXUPS,
FILE_LOADED, // Loaded by not yet active
FILE_LOAD_VERIFY_EXECUTION,
FILE_ACTIVE // Fully active (constructors run & security checked)
diff --git a/src/vm/ecall.cpp b/src/vm/ecall.cpp
index b8e0d64e8f..dfeff95d6f 100644
--- a/src/vm/ecall.cpp
+++ b/src/vm/ecall.cpp
@@ -29,6 +29,36 @@ extern const int c_nECClasses;
#endif // CROSSGEN_COMPILE
+/**********
+
+The constructors of string-like types (String, Utf8String) are special since the JIT will
+replace newobj instructions with calls to the corresponding 'Ctor' method. Depending on the
+CLR in use, the ctor methods may be instance methods (with a null 'this' parameter) or
+static methods. See the managed definitions of String.Ctor and Utf8String.Ctor for more
+information.
+
+To add a new ctor overload, in addition to defining the constructor and Ctor methods on
+the managed side, make changes to the following files. (These instructions are for
+Utf8String, but String is similar.)
+
+- src/vm/ecall.cpp (this file), update the definition of "NumberOfUtf8StringConstructors"
+ and add the appropriate static asserts immediately above the definition.
+
+- src/vm/ecall.h, search for "Utf8StringCtor" and add the DYNAMICALLY_ASSIGNED_FCALL_IMPL
+ definitions corresponding to the new overloads.
+
+- src/vm/ecalllist.h, search for "FCFuncStart(gUtf8StringFuncs)" and add the overloads
+ within that block.
+
+- src/vm/metasig.h, add the new Utf8String-returning metasig declarations; and, if necessary,
+ add any void-returning metasig declarations if they haven't already been defined elsewhere.
+ search "String_RetUtf8Str" for an example of how to do this.
+
+- src/vm/mscorlib.h, search "DEFINE_CLASS(UTF8_STRING" and add the new DEFINE_METHOD
+ declarations for the Utf8String-returning Ctor methods, referencing the new metasig declarations.
+
+**********/
+
// METHOD__STRING__CTORF_XXX has to be in same order as ECall::CtorCharXxx
#define METHOD__STRING__CTORF_FIRST METHOD__STRING__CTORF_CHARARRAY
static_assert_no_msg(METHOD__STRING__CTORF_FIRST + 0 == METHOD__STRING__CTORF_CHARARRAY);
@@ -55,14 +85,38 @@ static_assert_no_msg(ECallCtor_First + 8 == ECall::CtorSBytePtrStartLengthEncodi
#define NumberOfStringConstructors 9
+#ifdef FEATURE_UTF8STRING
+// METHOD__UTF8STRING__CTORF_XXX has to be in same order as ECall::Utf8StringCtorCharXxx
+#define METHOD__UTF8STRING__CTORF_FIRST METHOD__UTF8_STRING__CTORF_READONLYSPANOFBYTE
+static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 0 == METHOD__UTF8_STRING__CTORF_READONLYSPANOFBYTE);
+static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 1 == METHOD__UTF8_STRING__CTORF_READONLYSPANOFCHAR);
+static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 2 == METHOD__UTF8_STRING__CTORF_BYTEARRAY_START_LEN);
+static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 3 == METHOD__UTF8_STRING__CTORF_BYTEPTR);
+static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 4 == METHOD__UTF8_STRING__CTORF_CHARARRAY_START_LEN);
+static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 5 == METHOD__UTF8_STRING__CTORF_CHARPTR);
+static_assert_no_msg(METHOD__UTF8STRING__CTORF_FIRST + 6 == METHOD__UTF8_STRING__CTORF_STRING);
+
+// ECall::Utf8StringCtorCharXxx has to be in same order as METHOD__UTF8STRING__CTORF_XXX
+#define ECallUtf8String_Ctor_First ECall::Utf8StringCtorReadOnlySpanOfByteManaged
+static_assert_no_msg(ECallUtf8String_Ctor_First + 0 == ECall::Utf8StringCtorReadOnlySpanOfByteManaged);
+static_assert_no_msg(ECallUtf8String_Ctor_First + 1 == ECall::Utf8StringCtorReadOnlySpanOfCharManaged);
+static_assert_no_msg(ECallUtf8String_Ctor_First + 2 == ECall::Utf8StringCtorByteArrayStartLengthManaged);
+static_assert_no_msg(ECallUtf8String_Ctor_First + 3 == ECall::Utf8StringCtorBytePtrManaged);
+static_assert_no_msg(ECallUtf8String_Ctor_First + 4 == ECall::Utf8StringCtorCharArrayStartLengthManaged);
+static_assert_no_msg(ECallUtf8String_Ctor_First + 5 == ECall::Utf8StringCtorCharPtrManaged);
+static_assert_no_msg(ECallUtf8String_Ctor_First + 6 == ECall::Utf8StringCtorStringManaged);
+
+#define NumberOfUtf8StringConstructors 7
+#endif // FEATURE_UTF8STRING
+
void ECall::PopulateManagedStringConstructors()
{
STANDARD_VM_CONTRACT;
INDEBUG(static bool fInitialized = false);
_ASSERTE(!fInitialized); // assume this method is only called once
- _ASSERTE(g_pStringClass != NULL);
+ _ASSERTE(g_pStringClass != NULL);
for (int i = 0; i < NumberOfStringConstructors; i++)
{
MethodDesc* pMD = MscorlibBinder::GetMethod((BinderMethodID)(METHOD__STRING__CTORF_FIRST + i));
@@ -72,6 +126,20 @@ void ECall::PopulateManagedStringConstructors()
ECall::DynamicallyAssignFCallImpl(pDest, ECallCtor_First + i);
}
+
+#ifdef FEATURE_UTF8STRING
+ _ASSERTE(g_pUtf8StringClass != NULL);
+ for (int i = 0; i < NumberOfUtf8StringConstructors; i++)
+ {
+ MethodDesc* pMD = MscorlibBinder::GetMethod((BinderMethodID)(METHOD__UTF8STRING__CTORF_FIRST + i));
+ _ASSERTE(pMD != NULL);
+
+ PCODE pDest = pMD->GetMultiCallableAddrOfCode();
+
+ ECall::DynamicallyAssignFCallImpl(pDest, ECallUtf8String_Ctor_First + i);
+ }
+#endif // FEATURE_UTF8STRING
+
INDEBUG(fInitialized = true);
}
diff --git a/src/vm/ecall.h b/src/vm/ecall.h
index c809109c4c..58b4f0c34e 100644
--- a/src/vm/ecall.h
+++ b/src/vm/ecall.h
@@ -103,7 +103,7 @@ class ECall
static void EnumFCallMethods();
#endif // DACCESS_COMPILE
-#define DYNAMICALLY_ASSIGNED_FCALLS() \
+#define _DYNAMICALLY_ASSIGNED_FCALLS_BASE() \
DYNAMICALLY_ASSIGNED_FCALL_IMPL(FastAllocateString, FramedAllocateString) \
DYNAMICALLY_ASSIGNED_FCALL_IMPL(CtorCharArrayManaged, NULL) \
DYNAMICALLY_ASSIGNED_FCALL_IMPL(CtorCharArrayStartLengthManaged, NULL) \
@@ -116,6 +116,22 @@ class ECall
DYNAMICALLY_ASSIGNED_FCALL_IMPL(CtorSBytePtrStartLengthEncodingManaged, NULL) \
DYNAMICALLY_ASSIGNED_FCALL_IMPL(InternalGetCurrentThread, NULL) \
+#define _DYNAMICALLY_ASSIGNED_FCALLS_UTF8STRING() \
+ DYNAMICALLY_ASSIGNED_FCALL_IMPL(FastAllocateUtf8String, FramedAllocateUtf8String) \
+ DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorReadOnlySpanOfByteManaged, NULL) \
+ DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorReadOnlySpanOfCharManaged, NULL) \
+ DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorByteArrayStartLengthManaged, NULL) \
+ DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorBytePtrManaged, NULL) \
+ DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorCharArrayStartLengthManaged, NULL) \
+ DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorCharPtrManaged, NULL) \
+ DYNAMICALLY_ASSIGNED_FCALL_IMPL(Utf8StringCtorStringManaged, NULL) \
+
+#ifdef FEATURE_UTF8STRING
+#define DYNAMICALLY_ASSIGNED_FCALLS() _DYNAMICALLY_ASSIGNED_FCALLS_BASE() _DYNAMICALLY_ASSIGNED_FCALLS_UTF8STRING()
+#else
+#define DYNAMICALLY_ASSIGNED_FCALLS() _DYNAMICALLY_ASSIGNED_FCALLS_BASE()
+#endif // FEATURE_UTF8STRING
+
enum
{
#undef DYNAMICALLY_ASSIGNED_FCALL_IMPL
diff --git a/src/vm/ecalllist.h b/src/vm/ecalllist.h
index b44669ea75..ae98cff196 100644
--- a/src/vm/ecalllist.h
+++ b/src/vm/ecalllist.h
@@ -116,6 +116,19 @@ FCFuncStart(gStringFuncs)
FCFuncElement("Intern", AppDomainNative::GetOrInternString)
FCFuncEnd()
+#ifdef FEATURE_UTF8STRING
+FCFuncStart(gUtf8StringFuncs)
+ FCDynamic("FastAllocate", CORINFO_INTRINSIC_Illegal, ECall::FastAllocateUtf8String)
+ FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ReadOnlySpanOfByte_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorReadOnlySpanOfByteManaged)
+ FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ReadOnlySpanOfChar_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorReadOnlySpanOfCharManaged)
+ FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ArrByte_Int_Int_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorByteArrayStartLengthManaged)
+ FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrByte_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorBytePtrManaged)
+ FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_ArrChar_Int_Int_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorCharArrayStartLengthManaged)
+ FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_PtrChar_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorCharPtrManaged)
+ FCDynamicSig(COR_CTOR_METHOD_NAME, &gsig_IM_Str_RetVoid, CORINFO_INTRINSIC_Illegal, ECall::Utf8StringCtorStringManaged)
+FCFuncEnd()
+#endif // FEATURE_UTF8STRING
+
FCFuncStart(gValueTypeFuncs)
FCFuncElement("CanCompareBits", ValueTypeHelper::CanCompareBits)
FCFuncElement("FastEqualsCheck", ValueTypeHelper::FastEqualsCheck)
@@ -507,6 +520,9 @@ FCFuncStart(gAssemblyLoadContextFuncs)
QCFuncElement("LoadFromPath", AssemblyNative::LoadFromPath)
QCFuncElement("InternalLoadUnmanagedDllFromPath", AssemblyNative::InternalLoadUnmanagedDllFromPath)
QCFuncElement("LoadFromStream", AssemblyNative::LoadFromStream)
+#ifndef FEATURE_PAL
+ QCFuncElement("LoadFromInMemoryModuleInternal", AssemblyNative::LoadFromInMemoryModule)
+#endif
QCFuncElement("GetLoadContextForAssembly", AssemblyNative::GetLoadContextForAssembly)
FCFuncElement("GetLoadedAssemblies", AppDomainNative::GetLoadedAssemblies)
#if defined(FEATURE_MULTICOREJIT)
@@ -802,7 +818,6 @@ FCFuncStart(gInteropMarshalFuncs)
#ifdef FEATURE_COMINTEROP
FCFuncElement("GetHRForException", MarshalNative::GetHRForException)
- FCFuncElement("GetHRForException_WinRT", MarshalNative::GetHRForException_WinRT)
FCFuncElement("GetRawIUnknownForComObjectNoAddRef", MarshalNative::GetRawIUnknownForComObjectNoAddRef)
FCFuncElement("IsComObject", MarshalNative::IsComObject)
FCFuncElement("GetObjectForIUnknown", MarshalNative::GetObjectForIUnknown)
@@ -819,21 +834,26 @@ FCFuncStart(gInteropMarshalFuncs)
FCFuncElement("GetObjectsForNativeVariants", MarshalNative::GetObjectsForNativeVariants)
FCFuncElement("GetStartComSlot", MarshalNative::GetStartComSlot)
FCFuncElement("GetEndComSlot", MarshalNative::GetEndComSlot)
-
- FCFuncElement("InitializeManagedWinRTFactoryObject", MarshalNative::InitializeManagedWinRTFactoryObject)
-
- FCFuncElement("GetNativeActivationFactory", MarshalNative::GetNativeActivationFactory)
FCFuncElement("GetIUnknownForObjectNative", MarshalNative::GetIUnknownForObjectNative)
FCFuncElement("GetComInterfaceForObjectNative", MarshalNative::GetComInterfaceForObjectNative)
FCFuncElement("InternalReleaseComObject", MarshalNative::ReleaseComObject)
FCFuncElement("Release", MarshalNative::Release)
- FCFuncElement("InitializeWrapperForWinRT", MarshalNative::InitializeWrapperForWinRT)
FCFuncElement("GetTypedObjectForIUnknown", MarshalNative::GetTypedObjectForIUnknown)
FCFuncElement("ChangeWrapperHandleStrength", MarshalNative::ChangeWrapperHandleStrength)
FCFuncElement("CleanupUnusedObjectsInCurrentContext", MarshalNative::CleanupUnusedObjectsInCurrentContext)
#endif // FEATURE_COMINTEROP
FCFuncEnd()
+#ifdef FEATURE_COMINTEROP
+FCFuncStart(gWindowsRuntimeMarshalFuncs)
+ FCFuncElement("GetNativeActivationFactory", MarshalNative::GetNativeActivationFactory)
+ FCFuncElement("GetHRForException", MarshalNative::GetHRForException_WinRT)
+ FCFuncElement("GetUniqueObjectForIUnknownWithoutUnboxing", MarshalNative::GetUniqueObjectForIUnknownWithoutUnboxing)
+ FCFuncElement("InitializeManagedWinRTFactoryObject", MarshalNative::InitializeManagedWinRTFactoryObject)
+ FCFuncElement("InitializeWrapper", MarshalNative::InitializeWrapperForWinRT)
+FCFuncEnd()
+#endif
+
FCFuncStart(gInteropNativeLibraryFuncs)
QCFuncElement("LoadFromPath", NativeLibraryNative::LoadFromPath)
QCFuncElement("LoadByName", NativeLibraryNative::LoadByName)
@@ -1270,6 +1290,9 @@ FCClassElement("TypedReference", "System", gTypedReferenceFuncs)
#ifdef FEATURE_COMINTEROP
FCClassElement("UriMarshaler", "System.StubHelpers", gUriMarshalerFuncs)
#endif
+#ifdef FEATURE_UTF8STRING
+FCClassElement("Utf8String", "System", gUtf8StringFuncs)
+#endif // FEATURE_UTF8STRING
FCClassElement("ValueClassMarshaler", "System.StubHelpers", gValueClassMarshalerFuncs)
FCClassElement("ValueType", "System", gValueTypeFuncs)
#ifdef FEATURE_COMINTEROP
@@ -1281,6 +1304,7 @@ FCClassElement("WeakReference`1", "System", gWeakReferenceOfTFuncs)
#ifdef FEATURE_COMINTEROP
FCClassElement("WinRTTypeNameConverter", "System.StubHelpers", gWinRTTypeNameConverterFuncs)
+FCClassElement("WindowsRuntimeMarshal", "System.Runtime.InteropServices.WindowsRuntime", gWindowsRuntimeMarshalFuncs)
#endif // FEATURE_COMINTEROP
diff --git a/src/vm/eventpipe.cpp b/src/vm/eventpipe.cpp
index 4de0b3c943..634f1d0d69 100644
--- a/src/vm/eventpipe.cpp
+++ b/src/vm/eventpipe.cpp
@@ -8,6 +8,7 @@
#include "eventpipe.h"
#include "eventpipebuffermanager.h"
#include "eventpipeconfiguration.h"
+#include "eventpipesessionprovider.h"
#include "eventpipeevent.h"
#include "eventpipeeventsource.h"
#include "eventpipefile.h"
@@ -26,12 +27,12 @@
CrstStatic EventPipe::s_configCrst;
bool EventPipe::s_tracingInitialized = false;
-EventPipeConfiguration* EventPipe::s_pConfig = NULL;
-EventPipeSession* EventPipe::s_pSession = NULL;
-EventPipeBufferManager* EventPipe::s_pBufferManager = NULL;
+EventPipeConfiguration *EventPipe::s_pConfig = NULL;
+EventPipeSession *EventPipe::s_pSession = NULL;
+EventPipeBufferManager *EventPipe::s_pBufferManager = NULL;
LPCWSTR EventPipe::s_pOutputPath = NULL;
-EventPipeFile* EventPipe::s_pFile = NULL;
-EventPipeEventSource* EventPipe::s_pEventSource = NULL;
+EventPipeFile *EventPipe::s_pFile = NULL;
+EventPipeEventSource *EventPipe::s_pEventSource = NULL;
LPCWSTR EventPipe::s_pCommandLine = NULL;
unsigned long EventPipe::s_nextFileIndex;
HANDLE EventPipe::s_fileSwitchTimerHandle = NULL;
@@ -78,7 +79,7 @@ EventPipeEventPayload::EventPipeEventPayload(EventData *pEventData, unsigned int
m_allocatedData = false;
S_UINT32 tmp_size = S_UINT32(0);
- for (unsigned int i=0; i<m_eventDataCount; i++)
+ for (unsigned int i = 0; i < m_eventDataCount; i++)
{
tmp_size += S_UINT32(m_pEventData[i].Size);
}
@@ -106,7 +107,7 @@ EventPipeEventPayload::~EventPipeEventPayload()
}
CONTRACTL_END;
- if(m_allocatedData && m_pData != NULL)
+ if (m_allocatedData && m_pData != NULL)
{
delete[] m_pData;
m_pData = NULL;
@@ -123,11 +124,11 @@ void EventPipeEventPayload::Flatten()
}
CONTRACTL_END;
- if(m_size > 0)
+ if (m_size > 0)
{
if (!IsFlattened())
{
- BYTE* tmp_pData = new (nothrow) BYTE[m_size];
+ BYTE *tmp_pData = new (nothrow) BYTE[m_size];
if (tmp_pData != NULL)
{
m_allocatedData = true;
@@ -148,26 +149,26 @@ void EventPipeEventPayload::CopyData(BYTE *pDst)
}
CONTRACTL_END;
- if(m_size > 0)
+ if (m_size > 0)
{
- if(IsFlattened())
+ if (IsFlattened())
{
memcpy(pDst, m_pData, m_size);
}
- else if(m_pEventData != NULL)
+ else if (m_pEventData != NULL)
{
unsigned int offset = 0;
- for(unsigned int i=0; i<m_eventDataCount; i++)
+ for (unsigned int i = 0; i < m_eventDataCount; i++)
{
- memcpy(pDst + offset, (BYTE*) m_pEventData[i].Ptr, m_pEventData[i].Size);
+ memcpy(pDst + offset, (BYTE *)m_pEventData[i].Ptr, m_pEventData[i].Size);
offset += m_pEventData[i].Size;
}
}
}
}
-BYTE* EventPipeEventPayload::GetFlatData()
+BYTE *EventPipeEventPayload::GetFlatData()
{
CONTRACTL
{
@@ -222,7 +223,7 @@ void EventPipe::Shutdown()
{
Disable((EventPipeSessionID)s_pSession);
}
- EX_CATCH { }
+ EX_CATCH {}
EX_END_CATCH(SwallowAllExceptions);
// Save pointers to the configuration and buffer manager.
@@ -236,42 +237,48 @@ void EventPipe::Shutdown()
FlushProcessWriteBuffers();
// Free resources.
- delete(pConfig);
- delete(pBufferManager);
- delete(s_pEventSource);
+ delete pConfig;
+ delete pBufferManager;
+ delete s_pEventSource;
s_pEventSource = NULL;
- delete(s_pOutputPath);
+ delete[] s_pOutputPath;
s_pOutputPath = NULL;
// On Windows, this is just a pointer to the return value from
// GetCommandLineW(), so don't attempt to free it.
#ifdef FEATURE_PAL
- delete[](s_pCommandLine);
+ delete[] s_pCommandLine;
s_pCommandLine = NULL;
#endif
}
EventPipeSessionID EventPipe::Enable(
LPCWSTR strOutputPath,
- unsigned int circularBufferSizeInMB,
- EventPipeProviderConfiguration *pProviders,
- int numProviders,
- UINT64 multiFileTraceLengthInSeconds)
+ uint32_t circularBufferSizeInMB,
+ uint64_t profilerSamplingRateInNanoseconds,
+ const EventPipeProviderConfiguration *pProviders,
+ uint32_t numProviders,
+ uint64_t multiFileTraceLengthInSeconds)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
+ PRECONDITION((numProviders == 0) || (numProviders > 0 && pProviders != nullptr));
}
CONTRACTL_END;
+ // Take the lock before enabling tracing.
+ CrstHolder _crst(GetLock());
+
// Create a new session.
+ SampleProfiler::SetSamplingRate((unsigned long)profilerSamplingRateInNanoseconds);
EventPipeSession *pSession = s_pConfig->CreateSession(
(strOutputPath != NULL) ? EventPipeSessionType::File : EventPipeSessionType::Streaming,
circularBufferSizeInMB,
pProviders,
- static_cast<unsigned int>(numProviders),
+ numProviders,
multiFileTraceLengthInSeconds);
// Enable the session.
@@ -286,27 +293,21 @@ EventPipeSessionID EventPipe::Enable(LPCWSTR strOutputPath, EventPipeSession *pS
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(pSession != NULL);
+ PRECONDITION(GetLock()->OwnedByCurrentThread());
}
CONTRACTL_END;
// If tracing is not initialized or is already enabled, bail here.
- if(!s_tracingInitialized || s_pConfig == NULL || s_pConfig->Enabled())
- {
+ if (!s_tracingInitialized || s_pConfig == NULL || s_pConfig->Enabled())
return 0;
- }
// If the state or arguments are invalid, bail here.
- if(pSession == NULL || !pSession->IsValid())
- {
+ if (pSession == NULL || !pSession->IsValid())
return 0;
- }
// Enable the EventPipe EventSource.
s_pEventSource->Enable(pSession);
- // Take the lock before enabling tracing.
- CrstHolder _crst(GetLock());
-
// Initialize the next file index.
s_nextFileIndex = 1;
@@ -342,7 +343,7 @@ EventPipeSessionID EventPipe::Enable(LPCWSTR strOutputPath, EventPipeSession *pS
SampleProfiler::Enable();
// Enable the file switch timer if needed.
- if(s_pSession->GetMultiFileTraceLengthInSeconds() > 0)
+ if (s_pSession->GetMultiFileTraceLengthInSeconds() > 0)
{
CreateFileSwitchTimer();
}
@@ -363,7 +364,7 @@ void EventPipe::Disable(EventPipeSessionID id)
// Only perform the disable operation if the session ID
// matches the current active session.
- if(id != (EventPipeSessionID)s_pSession)
+ if (id != (EventPipeSessionID)s_pSession)
{
return;
}
@@ -374,7 +375,7 @@ void EventPipe::Disable(EventPipeSessionID id)
// Take the lock before disabling tracing.
CrstHolder _crst(GetLock());
- if(s_pConfig != NULL && s_pConfig->Enabled())
+ if (s_pConfig != NULL && s_pConfig->Enabled())
{
// Disable the profiler.
SampleProfiler::Disable();
@@ -399,30 +400,31 @@ void EventPipe::Disable(EventPipeSessionID id)
FlushProcessWriteBuffers();
// Write to the file.
- if(s_pFile != NULL)
+ if (s_pFile != NULL)
{
LARGE_INTEGER disableTimeStamp;
QueryPerformanceCounter(&disableTimeStamp);
s_pBufferManager->WriteAllBuffersToFile(s_pFile, disableTimeStamp);
- if(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeRundown) > 0)
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_EventPipeRundown) > 0)
{
// Before closing the file, do rundown.
- const unsigned int numRundownProviders = 2;
- EventPipeProviderConfiguration rundownProviders[] =
- {
- { W("Microsoft-Windows-DotNETRuntime"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose), NULL }, // Public provider.
- { W("Microsoft-Windows-DotNETRuntimeRundown"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose), NULL } // Rundown provider.
+ const EventPipeProviderConfiguration RundownProviders[] = {
+ {W("Microsoft-Windows-DotNETRuntime"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose), NULL}, // Public provider.
+ {W("Microsoft-Windows-DotNETRuntimeRundown"), 0x80020138, static_cast<unsigned int>(EventPipeEventLevel::Verbose), NULL} // Rundown provider.
};
+
// The circular buffer size doesn't matter because all events are written synchronously during rundown.
- s_pSession = s_pConfig->CreateSession(EventPipeSessionType::File, 1 /* circularBufferSizeInMB */, rundownProviders, numRundownProviders);
+ s_pSession = s_pConfig->CreateSession(
+ EventPipeSessionType::File,
+ 1 /* circularBufferSizeInMB */,
+ RundownProviders,
+ sizeof(RundownProviders) / sizeof(EventPipeProviderConfiguration));
s_pConfig->EnableRundown(s_pSession);
// Ask the runtime to emit rundown events.
- if(g_fEEStarted && !g_fEEShutDown)
- {
+ if (g_fEEStarted && !g_fEEShutDown)
ETW::EnumerationLog::EndRundown();
- }
// Disable the event pipe now that rundown is complete.
s_pConfig->Disable(s_pSession);
@@ -432,7 +434,7 @@ void EventPipe::Disable(EventPipeSessionID id)
s_pSession = NULL;
}
- delete(s_pFile);
+ delete s_pFile;
s_pFile = NULL;
}
@@ -456,7 +458,7 @@ void EventPipe::CreateFileSwitchTimer()
}
CONTRACTL_END
- NewHolder<ThreadpoolMgr::TimerInfoContext> timerContextHolder = new(nothrow) ThreadpoolMgr::TimerInfoContext();
+ NewHolder<ThreadpoolMgr::TimerInfoContext> timerContextHolder = new (nothrow) ThreadpoolMgr::TimerInfoContext();
if (timerContextHolder == NULL)
{
return;
@@ -503,7 +505,7 @@ void EventPipe::DeleteFileSwitchTimer()
}
CONTRACTL_END
- if((s_fileSwitchTimerHandle != NULL) && (ThreadpoolMgr::DeleteTimerQueueTimer(s_fileSwitchTimerHandle, NULL)))
+ if ((s_fileSwitchTimerHandle != NULL) && (ThreadpoolMgr::DeleteTimerQueueTimer(s_fileSwitchTimerHandle, NULL)))
{
s_fileSwitchTimerHandle = NULL;
}
@@ -520,25 +522,23 @@ void WINAPI EventPipe::SwitchToNextFileTimerCallback(PVOID parameter, BOOLEAN ti
}
CONTRACTL_END;
-
// Take the lock control lock to make sure that tracing isn't disabled during this operation.
CrstHolder _crst(GetLock());
// Make sure that we should actually switch files.
UINT64 multiFileTraceLengthInSeconds = s_pSession->GetMultiFileTraceLengthInSeconds();
- if(!Enabled() || s_pSession->GetSessionType() != EventPipeSessionType::File || multiFileTraceLengthInSeconds == 0)
+ if (!Enabled() || s_pSession->GetSessionType() != EventPipeSessionType::File || multiFileTraceLengthInSeconds == 0)
{
return;
}
GCX_PREEMP();
- if(CLRGetTickCount64() > (s_lastFileSwitchTime + (multiFileTraceLengthInSeconds * 1000)))
+ if (CLRGetTickCount64() > (s_lastFileSwitchTime + (multiFileTraceLengthInSeconds * 1000)))
{
SwitchToNextFile();
s_lastFileSwitchTime = CLRGetTickCount64();
}
-
}
void EventPipe::SwitchToNextFile()
@@ -562,14 +562,15 @@ void EventPipe::SwitchToNextFile()
// Open the new file.
SString nextTraceFilePath;
GetNextFilePath(s_pSession, nextTraceFilePath);
- EventPipeFile* pFile = new (nothrow) EventPipeFile(nextTraceFilePath);
- if(pFile == NULL)
+ EventPipeFile *pFile = new (nothrow) EventPipeFile(nextTraceFilePath);
+ if (pFile == NULL)
{
+ // TODO: Add error handling.
return;
}
// Close the previous file.
- delete(s_pFile);
+ delete s_pFile;
// Swap in the new file.
s_pFile = pFile;
@@ -592,11 +593,11 @@ void EventPipe::GetNextFilePath(EventPipeSession *pSession, SString &nextTraceFi
// If multiple files have been requested, then add a sequence number to the trace file name.
UINT64 multiFileTraceLengthInSeconds = pSession->GetMultiFileTraceLengthInSeconds();
- if(multiFileTraceLengthInSeconds > 0)
+ if (multiFileTraceLengthInSeconds > 0)
{
// Remove the ".netperf" file extension if it exists.
SString::Iterator netPerfExtension = nextTraceFilePath.End();
- if(nextTraceFilePath.FindBack(netPerfExtension, W(".netperf")))
+ if (nextTraceFilePath.FindBack(netPerfExtension, W(".netperf")))
{
nextTraceFilePath.Truncate(netPerfExtension);
}
@@ -608,12 +609,12 @@ void EventPipe::GetNextFilePath(EventPipeSession *pSession, SString &nextTraceFi
}
}
-EventPipeSession* EventPipe::GetSession(EventPipeSessionID id)
+EventPipeSession *EventPipe::GetSession(EventPipeSessionID id)
{
LIMITED_METHOD_CONTRACT;
EventPipeSession *pSession = NULL;
- if((EventPipeSessionID)s_pSession == id)
+ if ((EventPipeSessionID)s_pSession == id)
{
pSession = s_pSession;
}
@@ -625,7 +626,7 @@ bool EventPipe::Enabled()
LIMITED_METHOD_CONTRACT;
bool enabled = false;
- if(s_pConfig != NULL)
+ if (s_pConfig != NULL)
{
enabled = s_pConfig->Enabled();
}
@@ -633,7 +634,7 @@ bool EventPipe::Enabled()
return enabled;
}
-EventPipeProvider* EventPipe::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData)
+EventPipeProvider *EventPipe::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData)
{
CONTRACTL
{
@@ -650,10 +651,9 @@ EventPipeProvider* EventPipe::CreateProvider(const SString &providerName, EventP
}
return pProvider;
-
}
-EventPipeProvider* EventPipe::GetProvider(const SString &providerName)
+EventPipeProvider *EventPipe::GetProvider(const SString &providerName)
{
CONTRACTL
{
@@ -687,9 +687,9 @@ void EventPipe::DeleteProvider(EventPipeProvider *pProvider)
// where we hold a provider after tracing has been disabled.
CrstHolder _crst(GetLock());
- if(pProvider != NULL)
+ if (pProvider != NULL)
{
- if(Enabled())
+ if (Enabled())
{
// Save the provider until the end of the tracing session.
pProvider->SetDeleteDeferred();
@@ -744,7 +744,7 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload
CONTRACTL_END;
// Exit early if the event is not enabled.
- if(!event.IsEnabled())
+ if (!event.IsEnabled())
{
return;
}
@@ -752,7 +752,7 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload
// Get the current thread;
Thread *pThread = GetThread();
- if(s_pConfig == NULL)
+ if (s_pConfig == NULL)
{
// We can't procede without a configuration
return;
@@ -760,23 +760,23 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload
_ASSERTE(s_pSession != NULL);
// If the activity id isn't specified AND we are in a managed thread, pull it from the current thread.
- // If pThread is NULL (we aren't in writing from a managed thread) then pActivityId can be NULL
- if(pActivityId == NULL && pThread != NULL)
+ // If pThread is NULL (we aren't in writing from a managed thread) then pActivityId can be NULL
+ if (pActivityId == NULL && pThread != NULL)
{
pActivityId = pThread->GetActivityId();
}
- if(!s_pConfig->RundownEnabled() && s_pBufferManager != NULL)
+ if (!s_pConfig->RundownEnabled() && s_pBufferManager != NULL)
{
s_pBufferManager->WriteEvent(pThread, *s_pSession, event, payload, pActivityId, pRelatedActivityId);
}
- else if(s_pConfig->RundownEnabled())
+ else if (s_pConfig->RundownEnabled())
{
// It is possible that some events that are enabled on rundown can be emitted from other threads.
// We're not interested in these events and they can cause corrupted trace files because rundown
// events are written synchronously and not under lock.
// If we encounter an event that did not originate on the thread that is doing rundown, ignore it.
- if(pThread == NULL || !s_pConfig->IsRundownThread(pThread))
+ if (pThread == NULL || !s_pConfig->IsRundownThread(pThread))
{
return;
}
@@ -800,7 +800,7 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload
pRelatedActivityId);
instance.EnsureStack(*s_pSession);
- if(s_pFile != NULL)
+ if (s_pFile != NULL)
{
// EventPipeFile::WriteEvent needs to allocate a metadata event
// and can therefore throw. In this context we will silently
@@ -809,7 +809,7 @@ void EventPipe::WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload
{
s_pFile->WriteEvent(instance);
}
- EX_CATCH { }
+ EX_CATCH {}
EX_END_CATCH(SwallowAllExceptions);
}
}
@@ -829,7 +829,7 @@ void EventPipe::WriteSampleProfileEvent(Thread *pSamplingThread, EventPipeEvent
EventPipeEventPayload payload(pData, length);
// Write the event to the thread's buffer.
- if(s_pBufferManager != NULL)
+ if (s_pBufferManager != NULL)
{
// Specify the sampling thread as the "current thread", so that we select the right buffer.
// Specify the target thread so that the event gets properly attributed.
@@ -848,7 +848,7 @@ bool EventPipe::WalkManagedStackForCurrentThread(StackContents &stackContents)
CONTRACTL_END;
Thread *pThread = GetThread();
- if(pThread != NULL)
+ if (pThread != NULL)
{
return WalkManagedStackForThread(pThread, stackContents);
}
@@ -869,12 +869,12 @@ bool EventPipe::WalkManagedStackForThread(Thread *pThread, StackContents &stackC
// Calling into StackWalkFrames in preemptive mode violates the host contract,
// but this contract is not used on CoreCLR.
- CONTRACT_VIOLATION( HostViolation );
+ CONTRACT_VIOLATION(HostViolation);
stackContents.Reset();
StackWalkAction swaRet = pThread->StackWalkFrames(
- (PSTACKWALKFRAMESCALLBACK) &StackWalkCallback,
+ (PSTACKWALKFRAMESCALLBACK)&StackWalkCallback,
&stackContents,
ALLOW_ASYNC_STACK_WALK | FUNCTIONSONLY | HANDLESKIPPEDFRAMES | ALLOW_INVALID_OBJECTS);
@@ -895,9 +895,9 @@ StackWalkAction EventPipe::StackWalkCallback(CrawlFrame *pCf, StackContents *pDa
// Get the IP.
UINT_PTR controlPC = (UINT_PTR)pCf->GetRegisterSet()->ControlPC;
- if(controlPC == 0)
+ if (controlPC == 0)
{
- if(pData->GetLength() == 0)
+ if (pData->GetLength() == 0)
{
// This happens for pinvoke stubs on the top of the stack.
return SWA_CONTINUE;
@@ -909,21 +909,20 @@ StackWalkAction EventPipe::StackWalkCallback(CrawlFrame *pCf, StackContents *pDa
// Add the IP to the captured stack.
pData->Append(
controlPC,
- pCf->GetFunction()
- );
+ pCf->GetFunction());
// Continue the stack walk.
return SWA_CONTINUE;
}
-EventPipeConfiguration* EventPipe::GetConfiguration()
+EventPipeConfiguration *EventPipe::GetConfiguration()
{
LIMITED_METHOD_CONTRACT;
return s_pConfig;
}
-CrstStatic* EventPipe::GetLock()
+CrstStatic *EventPipe::GetLock()
{
LIMITED_METHOD_CONTRACT;
@@ -955,7 +954,7 @@ void EventPipe::SaveCommandLine(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR *argv
commandLine.Append((WCHAR)' ');
commandLine.Append(pwzAssemblyPath);
- for(int i=0; i<argc; i++)
+ for (int i = 0; i < argc; i++)
{
commandLine.Append((WCHAR)' ');
commandLine.Append(argv[i]);
@@ -971,7 +970,7 @@ void EventPipe::SaveCommandLine(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR *argv
#endif
}
-EventPipeEventInstance* EventPipe::GetNextEvent()
+EventPipeEventInstance *EventPipe::GetNextEvent()
{
CONTRACTL
{
@@ -993,253 +992,4 @@ EventPipeEventInstance* EventPipe::GetNextEvent()
return pInstance;
}
-UINT64 QCALLTYPE EventPipeInternal::Enable(
- __in_z LPCWSTR outputFile,
- UINT32 circularBufferSizeInMB,
- INT64 profilerSamplingRateInNanoseconds,
- EventPipeProviderConfiguration *pProviders,
- INT32 numProviders,
- UINT64 multiFileTraceLengthInSeconds)
-{
- QCALL_CONTRACT;
-
- UINT64 sessionID = 0;
-
- BEGIN_QCALL;
- SampleProfiler::SetSamplingRate((unsigned long)profilerSamplingRateInNanoseconds);
- sessionID = EventPipe::Enable(outputFile, circularBufferSizeInMB, pProviders, numProviders, multiFileTraceLengthInSeconds);
- END_QCALL;
-
- return sessionID;
-}
-
-void QCALLTYPE EventPipeInternal::Disable(UINT64 sessionID)
-{
- QCALL_CONTRACT;
-
- BEGIN_QCALL;
- EventPipe::Disable(sessionID);
- END_QCALL;
-}
-
-bool QCALLTYPE EventPipeInternal::GetSessionInfo(UINT64 sessionID, EventPipeSessionInfo *pSessionInfo)
-{
- QCALL_CONTRACT;
-
- bool retVal = false;
- BEGIN_QCALL;
-
- if(pSessionInfo != NULL)
- {
- EventPipeSession *pSession = EventPipe::GetSession(sessionID);
- if(pSession != NULL)
- {
- pSessionInfo->StartTimeAsUTCFileTime = pSession->GetStartTime();
- pSessionInfo->StartTimeStamp.QuadPart = pSession->GetStartTimeStamp().QuadPart;
- QueryPerformanceFrequency(&pSessionInfo->TimeStampFrequency);
- retVal = true;
- }
- }
-
- END_QCALL;
- return retVal;
-}
-
-INT_PTR QCALLTYPE EventPipeInternal::CreateProvider(
- __in_z LPCWSTR providerName,
- EventPipeCallback pCallbackFunc)
-{
- QCALL_CONTRACT;
-
- EventPipeProvider *pProvider = NULL;
-
- BEGIN_QCALL;
-
- pProvider = EventPipe::CreateProvider(providerName, pCallbackFunc, NULL);
-
- END_QCALL;
-
- return reinterpret_cast<INT_PTR>(pProvider);
-}
-
-INT_PTR QCALLTYPE EventPipeInternal::DefineEvent(
- INT_PTR provHandle,
- UINT32 eventID,
- __int64 keywords,
- UINT32 eventVersion,
- UINT32 level,
- void *pMetadata,
- UINT32 metadataLength)
-{
- QCALL_CONTRACT;
-
- EventPipeEvent *pEvent = NULL;
-
- BEGIN_QCALL;
-
- _ASSERTE(provHandle != NULL);
- EventPipeProvider *pProvider = reinterpret_cast<EventPipeProvider *>(provHandle);
- pEvent = pProvider->AddEvent(eventID, keywords, eventVersion, (EventPipeEventLevel)level, (BYTE *)pMetadata, metadataLength);
- _ASSERTE(pEvent != NULL);
-
- END_QCALL;
-
- return reinterpret_cast<INT_PTR>(pEvent);
-}
-
-INT_PTR QCALLTYPE EventPipeInternal::GetProvider(
- __in_z LPCWSTR providerName)
-{
- QCALL_CONTRACT;
-
- EventPipeProvider *pProvider = NULL;
-
- BEGIN_QCALL;
-
- pProvider = EventPipe::GetProvider(providerName);
-
- END_QCALL;
-
- return reinterpret_cast<INT_PTR>(pProvider);
-}
-
-void QCALLTYPE EventPipeInternal::DeleteProvider(
- INT_PTR provHandle)
-{
- QCALL_CONTRACT;
- BEGIN_QCALL;
-
- if(provHandle != NULL)
- {
- EventPipeProvider *pProvider = reinterpret_cast<EventPipeProvider*>(provHandle);
- EventPipe::DeleteProvider(pProvider);
- }
-
- END_QCALL;
-}
-
-int QCALLTYPE EventPipeInternal::EventActivityIdControl(
- uint controlCode,
- GUID *pActivityId)
-{
-
- QCALL_CONTRACT;
-
- int retVal = 0;
-
- BEGIN_QCALL;
-
- Thread *pThread = GetThread();
- if(pThread == NULL || pActivityId == NULL)
- {
- retVal = 1;
- }
- else
- {
- ActivityControlCode activityControlCode = (ActivityControlCode)controlCode;
- GUID currentActivityId;
- switch(activityControlCode)
- {
- case ActivityControlCode::EVENT_ACTIVITY_CONTROL_GET_ID:
-
- *pActivityId = *pThread->GetActivityId();
- break;
-
- case ActivityControlCode::EVENT_ACTIVITY_CONTROL_SET_ID:
-
- pThread->SetActivityId(pActivityId);
- break;
-
- case ActivityControlCode::EVENT_ACTIVITY_CONTROL_CREATE_ID:
-
- CoCreateGuid(pActivityId);
- break;
-
- case ActivityControlCode::EVENT_ACTIVITY_CONTROL_GET_SET_ID:
-
- currentActivityId = *pThread->GetActivityId();
- pThread->SetActivityId(pActivityId);
- *pActivityId = currentActivityId;
-
- break;
-
- case ActivityControlCode::EVENT_ACTIVITY_CONTROL_CREATE_SET_ID:
-
- *pActivityId = *pThread->GetActivityId();
- CoCreateGuid(&currentActivityId);
- pThread->SetActivityId(&currentActivityId);
- break;
-
- default:
- retVal = 1;
- };
- }
-
- END_QCALL;
- return retVal;
-}
-
-void QCALLTYPE EventPipeInternal::WriteEvent(
- INT_PTR eventHandle,
- UINT32 eventID,
- void *pData,
- UINT32 length,
- LPCGUID pActivityId,
- LPCGUID pRelatedActivityId)
-{
- QCALL_CONTRACT;
- BEGIN_QCALL;
-
- _ASSERTE(eventHandle != NULL);
- EventPipeEvent *pEvent = reinterpret_cast<EventPipeEvent *>(eventHandle);
- EventPipe::WriteEvent(*pEvent, (BYTE *)pData, length, pActivityId, pRelatedActivityId);
-
- END_QCALL;
-}
-
-void QCALLTYPE EventPipeInternal::WriteEventData(
- INT_PTR eventHandle,
- UINT32 eventID,
- EventData *pEventData,
- UINT32 eventDataCount,
- LPCGUID pActivityId,
- LPCGUID pRelatedActivityId)
-{
- QCALL_CONTRACT;
- BEGIN_QCALL;
-
- _ASSERTE(eventHandle != NULL);
- EventPipeEvent *pEvent = reinterpret_cast<EventPipeEvent *>(eventHandle);
- EventPipe::WriteEvent(*pEvent, pEventData, eventDataCount, pActivityId, pRelatedActivityId);
-
- END_QCALL;
-}
-
-bool QCALLTYPE EventPipeInternal::GetNextEvent(
- EventPipeEventInstanceData *pInstance)
-{
- QCALL_CONTRACT;
-
- EventPipeEventInstance *pNextInstance = NULL;
- BEGIN_QCALL;
-
- _ASSERTE(pInstance != NULL);
-
- pNextInstance = EventPipe::GetNextEvent();
- if (pNextInstance)
- {
- pInstance->ProviderID = pNextInstance->GetEvent()->GetProvider();
- pInstance->EventID = pNextInstance->GetEvent()->GetEventID();
- pInstance->ThreadID = pNextInstance->GetThreadId();
- pInstance->TimeStamp.QuadPart = pNextInstance->GetTimeStamp()->QuadPart;
- pInstance->ActivityId = *pNextInstance->GetActivityId();
- pInstance->RelatedActivityId = *pNextInstance->GetRelatedActivityId();
- pInstance->Payload = pNextInstance->GetData();
- pInstance->PayloadLength = pNextInstance->GetDataLength();
- }
-
- END_QCALL;
- return pNextInstance != NULL;
-}
-
#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipe.h b/src/vm/eventpipe.h
index c77b94dbdc..494a8c5c98 100644
--- a/src/vm/eventpipe.h
+++ b/src/vm/eventpipe.h
@@ -14,8 +14,6 @@ class EventPipeConfiguration;
class EventPipeEvent;
class EventPipeEventInstance;
class EventPipeFile;
-class EventPipeJsonFile;
-class EventPipeBuffer;
class EventPipeBufferManager;
class EventPipeEventSource;
class EventPipeProvider;
@@ -35,13 +33,13 @@ struct EventFilterDescriptor
ULONGLONG Ptr;
// The size of the filter data, in bytes. The maximum size is 1024 bytes.
- ULONG Size;
+ ULONG Size;
// The type of filter data. The type is application-defined. An event
// controller that knows about the provider and knows details about the
// provider's events can use the Type field to send the provider an
// arbitrary set of data for use as enhancements to the filtering of events.
- ULONG Type;
+ ULONG Type;
};
// Define the event pipe callback to match the ETW callback signature.
@@ -56,7 +54,6 @@ typedef void (*EventPipeCallback)(
struct EventData
{
-public:
UINT64 Ptr;
unsigned int Size;
unsigned int Reserved;
@@ -91,7 +88,7 @@ public:
// Get the flat formatted data in this payload
// This method will allocate a buffer if it does not already contain flattened data
// This method will return NULL on OOM if a buffer needed to be allocated
- BYTE* GetFlatData();
+ BYTE *GetFlatData();
// Return true is the data is stored in a flat buffer
bool IsFlattened() const
@@ -109,7 +106,7 @@ public:
return m_size;
}
- EventData* GetEventDataArray() const
+ EventData *GetEventDataArray() const
{
LIMITED_METHOD_CONTRACT;
@@ -120,7 +117,6 @@ public:
class StackContents
{
private:
-
const static unsigned int MAX_STACK_DEPTH = 100;
// Array of IP values from a stack crawl.
@@ -130,14 +126,13 @@ private:
#ifdef _DEBUG
// Parallel array of MethodDesc pointers.
// Used for debug-only stack printing.
- MethodDesc* m_methods[MAX_STACK_DEPTH];
+ MethodDesc *m_methods[MAX_STACK_DEPTH];
#endif // _DEBUG
// The next available slot in StackFrames.
unsigned int m_nextAvailableFrame;
public:
-
StackContents()
{
LIMITED_METHOD_CONTRACT;
@@ -152,7 +147,7 @@ public:
memcpy_s(pDest->m_stackFrames, MAX_STACK_DEPTH * sizeof(UINT_PTR), m_stackFrames, sizeof(UINT_PTR) * m_nextAvailableFrame);
#ifdef _DEBUG
- memcpy_s(pDest->m_methods, MAX_STACK_DEPTH * sizeof(MethodDesc*), m_methods, sizeof(MethodDesc*) * m_nextAvailableFrame);
+ memcpy_s(pDest->m_methods, MAX_STACK_DEPTH * sizeof(MethodDesc *), m_methods, sizeof(MethodDesc *) * m_nextAvailableFrame);
#endif
pDest->m_nextAvailableFrame = m_nextAvailableFrame;
}
@@ -192,7 +187,7 @@ public:
}
#ifdef _DEBUG
- MethodDesc* GetMethod(unsigned int frameIndex)
+ MethodDesc *GetMethod(unsigned int frameIndex)
{
LIMITED_METHOD_CONTRACT;
_ASSERTE(frameIndex < MAX_STACK_DEPTH);
@@ -210,7 +205,7 @@ public:
{
LIMITED_METHOD_CONTRACT;
- if(m_nextAvailableFrame < MAX_STACK_DEPTH)
+ if (m_nextAvailableFrame < MAX_STACK_DEPTH)
{
m_stackFrames[m_nextAvailableFrame] = controlPC;
#ifdef _DEBUG
@@ -220,11 +215,11 @@ public:
}
}
- BYTE* GetPointer() const
+ BYTE *GetPointer() const
{
LIMITED_METHOD_CONTRACT;
- return (BYTE*)m_stackFrames;
+ return (BYTE *)m_stackFrames;
}
unsigned int GetSize() const
@@ -246,146 +241,128 @@ class EventPipe
friend class EventPipeBufferManager;
friend class SampleProfiler;
- public:
-
- // Initialize the event pipe.
- static void Initialize();
-
- // Shutdown the event pipe.
- static void Shutdown();
-
- // Enable tracing via the event pipe.
- static EventPipeSessionID Enable(
- LPCWSTR strOutputPath,
- unsigned int circularBufferSizeInMB,
- EventPipeProviderConfiguration *pProviders,
- int numProviders,
- UINT64 multiFileTraceLengthInSeconds);
-
- // Disable tracing via the event pipe.
- static void Disable(EventPipeSessionID id);
-
- // Get the session for the specified session ID.
- static EventPipeSession* GetSession(EventPipeSessionID id);
-
- // Specifies whether or not the event pipe is enabled.
- static bool Enabled();
-
- // Create a provider.
- static EventPipeProvider* CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction = NULL, void *pCallbackData = NULL);
-
- // Get a provider.
- static EventPipeProvider* GetProvider(const SString &providerName);
-
- // Delete a provider.
- static void DeleteProvider(EventPipeProvider *pProvider);
-
- // Write out an event from a flat buffer.
- // Data is written as a serialized blob matching the ETW serialization conventions.
- static void WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int length, LPCGUID pActivityId = NULL, LPCGUID pRelatedActivityId = NULL);
-
- // Write out an event from an EventData array.
- // Data is written as a serialized blob matching the ETW serialization conventions.
- static void WriteEvent(EventPipeEvent &event, EventData *pEventData, unsigned int eventDataCount, LPCGUID pActivityId = NULL, LPCGUID pRelatedActivityId = NULL);
-
- // Write out a sample profile event.
- static void WriteSampleProfileEvent(Thread *pSamplingThread, EventPipeEvent *pEvent, Thread *pTargetThread, StackContents &stackContents, BYTE *pData = NULL, unsigned int length = 0);
-
- // Get the managed call stack for the current thread.
- static bool WalkManagedStackForCurrentThread(StackContents &stackContents);
+public:
+ // Initialize the event pipe.
+ static void Initialize();
- // Get the managed call stack for the specified thread.
- static bool WalkManagedStackForThread(Thread *pThread, StackContents &stackContents);
+ // Shutdown the event pipe.
+ static void Shutdown();
- // Save the command line for the current process.
- static void SaveCommandLine(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR *argv);
+ // Enable tracing via the event pipe.
+ static EventPipeSessionID Enable(
+ LPCWSTR strOutputPath,
+ uint32_t circularBufferSizeInMB,
+ uint64_t profilerSamplingRateInNanoseconds,
+ const EventPipeProviderConfiguration *pProviders,
+ uint32_t numProviders,
+ uint64_t multiFileTraceLengthInSeconds);
- // Get next event.
- static EventPipeEventInstance* GetNextEvent();
+ // Disable tracing via the event pipe.
+ static void Disable(EventPipeSessionID id);
- protected:
+ // Get the session for the specified session ID.
+ static EventPipeSession *GetSession(EventPipeSessionID id);
- // The counterpart to WriteEvent which after the payload is constructed
- static void WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId = NULL, LPCGUID pRelatedActivityId = NULL);
+ // Specifies whether or not the event pipe is enabled.
+ static bool Enabled();
- private:
+ // Create a provider.
+ static EventPipeProvider *CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction = NULL, void *pCallbackData = NULL);
- // Enable the specified EventPipe session.
- static EventPipeSessionID Enable(LPCWSTR strOutputPath, EventPipeSession *pSession);
+ // Get a provider.
+ static EventPipeProvider *GetProvider(const SString &providerName);
- static void CreateFileSwitchTimer();
+ // Delete a provider.
+ static void DeleteProvider(EventPipeProvider *pProvider);
- static void DeleteFileSwitchTimer();
+ // Write out an event from a flat buffer.
+ // Data is written as a serialized blob matching the ETW serialization conventions.
+ static void WriteEvent(EventPipeEvent &event, BYTE *pData, unsigned int length, LPCGUID pActivityId = NULL, LPCGUID pRelatedActivityId = NULL);
- // Performs one polling operation to determine if it is necessary to switch to a new file.
- // If the polling operation decides it is time, it will perform the switch.
- // Called directly from the timer when the timer is triggered.
- static void WINAPI SwitchToNextFileTimerCallback(PVOID parameter, BOOLEAN timerFired);
+ // Write out an event from an EventData array.
+ // Data is written as a serialized blob matching the ETW serialization conventions.
+ static void WriteEvent(EventPipeEvent &event, EventData *pEventData, unsigned int eventDataCount, LPCGUID pActivityId = NULL, LPCGUID pRelatedActivityId = NULL);
- // If event pipe has been configured to write multiple files, switch to the next file.
- static void SwitchToNextFile();
+ // Write out a sample profile event.
+ static void WriteSampleProfileEvent(Thread *pSamplingThread, EventPipeEvent *pEvent, Thread *pTargetThread, StackContents &stackContents, BYTE *pData = NULL, unsigned int length = 0);
- // Generate the file path for the next trace file.
- // This is used when event pipe has been configured to create multiple trace files with a specified maximum length of time.
- static void GetNextFilePath(EventPipeSession *pSession, SString &nextTraceFilePath);
+ // Get the managed call stack for the current thread.
+ static bool WalkManagedStackForCurrentThread(StackContents &stackContents);
- // Callback function for the stack walker. For each frame walked, this callback is invoked.
- static StackWalkAction StackWalkCallback(CrawlFrame *pCf, StackContents *pData);
+ // Get the managed call stack for the specified thread.
+ static bool WalkManagedStackForThread(Thread *pThread, StackContents &stackContents);
- // Get the configuration object.
- // This is called directly by the EventPipeProvider constructor to register the new provider.
- static EventPipeConfiguration* GetConfiguration();
+ // Save the command line for the current process.
+ static void SaveCommandLine(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR *argv);
- // Get the event pipe configuration lock.
- static CrstStatic* GetLock();
+ // Get next event.
+ static EventPipeEventInstance *GetNextEvent();
- static CrstStatic s_configCrst;
- static bool s_tracingInitialized;
- static EventPipeConfiguration *s_pConfig;
- static EventPipeSession *s_pSession;
- static EventPipeBufferManager *s_pBufferManager;
- static LPCWSTR s_pOutputPath;
- static unsigned long s_nextFileIndex;
- static EventPipeFile *s_pFile;
- static EventPipeEventSource *s_pEventSource;
- static LPCWSTR s_pCommandLine;
- const static DWORD FileSwitchTimerPeriodMS = 1000;
- static HANDLE s_fileSwitchTimerHandle;
- static ULONGLONG s_lastFileSwitchTime;
+private:
+ // The counterpart to WriteEvent which after the payload is constructed
+ static void WriteEventInternal(EventPipeEvent &event, EventPipeEventPayload &payload, LPCGUID pActivityId = NULL, LPCGUID pRelatedActivityId = NULL);
+
+ // Enable the specified EventPipe session.
+ static EventPipeSessionID Enable(LPCWSTR strOutputPath, EventPipeSession *pSession);
+
+ static void CreateFileSwitchTimer();
+
+ static void DeleteFileSwitchTimer();
+
+ // Performs one polling operation to determine if it is necessary to switch to a new file.
+ // If the polling operation decides it is time, it will perform the switch.
+ // Called directly from the timer when the timer is triggered.
+ static void WINAPI SwitchToNextFileTimerCallback(PVOID parameter, BOOLEAN timerFired);
+
+ // If event pipe has been configured to write multiple files, switch to the next file.
+ static void SwitchToNextFile();
+
+ // Generate the file path for the next trace file.
+ // This is used when event pipe has been configured to create multiple trace files with a specified maximum length of time.
+ static void GetNextFilePath(EventPipeSession *pSession, SString &nextTraceFilePath);
+
+ // Callback function for the stack walker. For each frame walked, this callback is invoked.
+ static StackWalkAction StackWalkCallback(CrawlFrame *pCf, StackContents *pData);
+
+ // Get the configuration object.
+ // This is called directly by the EventPipeProvider constructor to register the new provider.
+ static EventPipeConfiguration *GetConfiguration();
+
+ // Get the event pipe configuration lock.
+ static CrstStatic *GetLock();
+
+ static CrstStatic s_configCrst;
+ static bool s_tracingInitialized;
+ static EventPipeConfiguration *s_pConfig;
+ static EventPipeSession *s_pSession;
+ static EventPipeBufferManager *s_pBufferManager;
+ static LPCWSTR s_pOutputPath;
+ static unsigned long s_nextFileIndex;
+ static EventPipeFile *s_pFile;
+ static EventPipeEventSource *s_pEventSource;
+ static LPCWSTR s_pCommandLine;
+ const static DWORD FileSwitchTimerPeriodMS = 1000;
+ static HANDLE s_fileSwitchTimerHandle;
+ static ULONGLONG s_lastFileSwitchTime;
};
struct EventPipeProviderConfiguration
{
-
private:
-
- LPCWSTR m_pProviderName;
- UINT64 m_keywords;
- UINT32 m_loggingLevel;
- LPCWSTR m_pFilterData;
+ LPCWSTR m_pProviderName = nullptr;
+ UINT64 m_keywords = 0;
+ UINT32 m_loggingLevel = 0;
+ LPCWSTR m_pFilterData = nullptr;
public:
+ EventPipeProviderConfiguration() = default;
- EventPipeProviderConfiguration()
+ EventPipeProviderConfiguration(LPCWSTR pProviderName, UINT64 keywords, UINT32 loggingLevel, LPCWSTR pFilterData) :
+ m_pProviderName(pProviderName),
+ m_keywords(keywords),
+ m_loggingLevel(loggingLevel),
+ m_pFilterData(pFilterData)
{
- LIMITED_METHOD_CONTRACT;
- m_pProviderName = NULL;
- m_keywords = NULL;
- m_loggingLevel = 0;
- m_pFilterData = NULL;
- }
-
- EventPipeProviderConfiguration(
- LPCWSTR pProviderName,
- UINT64 keywords,
- UINT32 loggingLevel,
- LPCWSTR pFilterData)
- {
- LIMITED_METHOD_CONTRACT;
- m_pProviderName = pProviderName;
- m_keywords = keywords;
- m_loggingLevel = loggingLevel;
- m_pFilterData = pFilterData;
}
LPCWSTR GetProviderName() const
@@ -413,95 +390,6 @@ public:
}
};
-class EventPipeInternal
-{
-private:
-
- enum class ActivityControlCode
- {
- EVENT_ACTIVITY_CONTROL_GET_ID = 1,
- EVENT_ACTIVITY_CONTROL_SET_ID = 2,
- EVENT_ACTIVITY_CONTROL_CREATE_ID = 3,
- EVENT_ACTIVITY_CONTROL_GET_SET_ID = 4,
- EVENT_ACTIVITY_CONTROL_CREATE_SET_ID = 5
- };
-
- struct EventPipeEventInstanceData
- {
- public:
- void *ProviderID;
- unsigned int EventID;
- unsigned int ThreadID;
- LARGE_INTEGER TimeStamp;
- GUID ActivityId;
- GUID RelatedActivityId;
- const BYTE *Payload;
- unsigned int PayloadLength;
- };
-
- struct EventPipeSessionInfo
- {
- public:
- FILETIME StartTimeAsUTCFileTime;
- LARGE_INTEGER StartTimeStamp;
- LARGE_INTEGER TimeStampFrequency;
- };
-
-public:
-
- static UINT64 QCALLTYPE Enable(
- __in_z LPCWSTR outputFile,
- UINT32 circularBufferSizeInMB,
- INT64 profilerSamplingRateInNanoseconds,
- EventPipeProviderConfiguration *pProviders,
- INT32 numProviders,
- UINT64 multiFileTraceLengthInSeconds);
-
- static void QCALLTYPE Disable(UINT64 sessionID);
-
- static bool QCALLTYPE GetSessionInfo(UINT64 sessionID, EventPipeSessionInfo *pSessionInfo);
-
- static INT_PTR QCALLTYPE CreateProvider(
- __in_z LPCWSTR providerName,
- EventPipeCallback pCallbackFunc);
-
- static INT_PTR QCALLTYPE DefineEvent(
- INT_PTR provHandle,
- UINT32 eventID,
- __int64 keywords,
- UINT32 eventVersion,
- UINT32 level,
- void *pMetadata,
- UINT32 metadataLength);
-
- static INT_PTR QCALLTYPE GetProvider(
- __in_z LPCWSTR providerName);
-
- static void QCALLTYPE DeleteProvider(
- INT_PTR provHandle);
-
- static int QCALLTYPE EventActivityIdControl(
- uint controlCode,
- GUID *pActivityId);
-
- static void QCALLTYPE WriteEvent(
- INT_PTR eventHandle,
- UINT32 eventID,
- void *pData,
- UINT32 length,
- LPCGUID pActivityId, LPCGUID pRelatedActivityId);
-
- static void QCALLTYPE WriteEventData(
- INT_PTR eventHandle,
- UINT32 eventID,
- EventData *pEventData,
- UINT32 eventDataCount,
- LPCGUID pActivityId, LPCGUID pRelatedActivityId);
-
- static bool QCALLTYPE GetNextEvent(
- EventPipeEventInstanceData *pInstance);
-};
-
#endif // FEATURE_PERFTRACING
#endif // __EVENTPIPE_H__
diff --git a/src/vm/eventpipeconfiguration.cpp b/src/vm/eventpipeconfiguration.cpp
index 9cf8280722..4667276753 100644
--- a/src/vm/eventpipeconfiguration.cpp
+++ b/src/vm/eventpipeconfiguration.cpp
@@ -6,12 +6,13 @@
#include "eventpipe.h"
#include "eventpipeconfiguration.h"
#include "eventpipeeventinstance.h"
+#include "eventpipesessionprovider.h"
#include "eventpipeprovider.h"
#include "eventpipesession.h"
#ifdef FEATURE_PERFTRACING
-const WCHAR* EventPipeConfiguration::s_configurationProviderName = W("Microsoft-DotNETCore-EventPipeConfiguration");
+const WCHAR *EventPipeConfiguration::s_configurationProviderName = W("Microsoft-DotNETCore-EventPipeConfiguration");
EventPipeConfiguration::EventPipeConfiguration()
{
@@ -22,7 +23,7 @@ EventPipeConfiguration::EventPipeConfiguration()
m_pRundownThread = NULL;
m_pConfigProvider = NULL;
m_pSession = NULL;
- m_pProviderList = new SList<SListElem<EventPipeProvider*>>();
+ m_pProviderList = new SList<SListElem<EventPipeProvider *>>();
}
EventPipeConfiguration::~EventPipeConfiguration()
@@ -35,25 +36,25 @@ EventPipeConfiguration::~EventPipeConfiguration()
}
CONTRACTL_END;
- if(m_pConfigProvider != NULL)
+ if (m_pConfigProvider != NULL)
{
// This unregisters the provider, which takes a
// HOST_BREAKABLE lock
EX_TRY
{
- DeleteProvider(m_pConfigProvider);
- m_pConfigProvider = NULL;
+ DeleteProvider(m_pConfigProvider);
+ m_pConfigProvider = NULL;
}
- EX_CATCH { }
+ EX_CATCH {}
EX_END_CATCH(SwallowAllExceptions);
}
- if(m_pSession != NULL)
+ if (m_pSession != NULL)
{
DeleteSession(m_pSession);
m_pSession = NULL;
}
- if(m_pProviderList != NULL)
+ if (m_pProviderList != NULL)
{
// We swallow exceptions here because the HOST_BREAKABLE
// lock may throw and this destructor gets called in throw
@@ -63,18 +64,18 @@ EventPipeConfiguration::~EventPipeConfiguration()
// Take the lock before manipulating the list.
CrstHolder _crst(EventPipe::GetLock());
- SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
- while(pElem != NULL)
+ SListElem<EventPipeProvider *> *pElem = m_pProviderList->GetHead();
+ while (pElem != NULL)
{
// We don't delete provider itself because it can be in-use
- SListElem<EventPipeProvider*> *pCurElem = pElem;
+ SListElem<EventPipeProvider *> *pCurElem = pElem;
pElem = m_pProviderList->GetNext(pElem);
- delete(pCurElem);
+ delete (pCurElem);
}
- delete(m_pProviderList);
+ delete (m_pProviderList);
}
- EX_CATCH { }
+ EX_CATCH {}
EX_END_CATCH(SwallowAllExceptions);
m_pProviderList = NULL;
@@ -96,14 +97,14 @@ void EventPipeConfiguration::Initialize()
// Create the metadata event.
m_pMetadataEvent = m_pConfigProvider->AddEvent(
- 0, /* eventID */
- 0, /* keywords */
- 0, /* eventVersion */
+ 0, /* eventID */
+ 0, /* keywords */
+ 0, /* eventVersion */
EventPipeEventLevel::LogAlways,
false); /* needStack */
}
-EventPipeProvider* EventPipeConfiguration::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData)
+EventPipeProvider *EventPipeConfiguration::CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData)
{
CONTRACTL
{
@@ -134,18 +135,15 @@ void EventPipeConfiguration::DeleteProvider(EventPipeProvider *pProvider)
CONTRACTL_END;
if (pProvider == NULL)
- {
return;
- }
// Unregister the provider.
UnregisterProvider(*pProvider);
// Free the provider itself.
- delete(pProvider);
+ delete pProvider;
}
-
bool EventPipeConfiguration::RegisterProvider(EventPipeProvider &provider)
{
CONTRACTL
@@ -161,7 +159,7 @@ bool EventPipeConfiguration::RegisterProvider(EventPipeProvider &provider)
// See if we've already registered this provider.
EventPipeProvider *pExistingProvider = GetProviderNoLock(provider.GetProviderName());
- if(pExistingProvider != NULL)
+ if (pExistingProvider != NULL)
{
return false;
}
@@ -170,14 +168,14 @@ bool EventPipeConfiguration::RegisterProvider(EventPipeProvider &provider)
if (m_pProviderList != NULL)
{
// The provider has not been registered, so register it.
- m_pProviderList->InsertTail(new SListElem<EventPipeProvider*>(&provider));
+ m_pProviderList->InsertTail(new SListElem<EventPipeProvider *>(&provider));
}
// Set the provider configuration and enable it if it has been requested by a session.
- if(m_pSession != NULL)
+ if (m_pSession != NULL)
{
EventPipeSessionProvider *pSessionProvider = GetSessionProvider(m_pSession, &provider);
- if(pSessionProvider != NULL)
+ if (pSessionProvider != NULL)
{
provider.SetConfiguration(
true /* providerEnabled */,
@@ -207,10 +205,10 @@ bool EventPipeConfiguration::UnregisterProvider(EventPipeProvider &provider)
if (m_pProviderList != NULL)
{
// Find the provider.
- SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
- while(pElem != NULL)
+ SListElem<EventPipeProvider *> *pElem = m_pProviderList->GetHead();
+ while (pElem != NULL)
{
- if(pElem->GetValue() == &provider)
+ if (pElem->GetValue() == &provider)
{
break;
}
@@ -219,11 +217,11 @@ bool EventPipeConfiguration::UnregisterProvider(EventPipeProvider &provider)
}
// If we found the provider, remove it.
- if(pElem != NULL)
+ if (pElem != NULL)
{
- if(m_pProviderList->FindAndRemove(pElem) != NULL)
+ if (m_pProviderList->FindAndRemove(pElem) != NULL)
{
- delete(pElem);
+ delete (pElem);
return true;
}
}
@@ -232,7 +230,7 @@ bool EventPipeConfiguration::UnregisterProvider(EventPipeProvider &provider)
return false;
}
-EventPipeProvider* EventPipeConfiguration::GetProvider(const SString &providerName)
+EventPipeProvider *EventPipeConfiguration::GetProvider(const SString &providerName)
{
CONTRACTL
{
@@ -249,7 +247,7 @@ EventPipeProvider* EventPipeConfiguration::GetProvider(const SString &providerNa
return GetProviderNoLock(providerName);
}
-EventPipeProvider* EventPipeConfiguration::GetProviderNoLock(const SString &providerName)
+EventPipeProvider *EventPipeConfiguration::GetProviderNoLock(const SString &providerName)
{
CONTRACTL
{
@@ -263,11 +261,11 @@ EventPipeProvider* EventPipeConfiguration::GetProviderNoLock(const SString &prov
// The provider list should be non-NULL, but can be NULL on shutdown.
if (m_pProviderList != NULL)
{
- SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
- while(pElem != NULL)
+ SListElem<EventPipeProvider *> *pElem = m_pProviderList->GetHead();
+ while (pElem != NULL)
{
EventPipeProvider *pProvider = pElem->GetValue();
- if(pProvider->GetProviderName().Equals(providerName))
+ if (pProvider->GetProviderName().Equals(providerName))
{
return pProvider;
}
@@ -279,7 +277,7 @@ EventPipeProvider* EventPipeConfiguration::GetProviderNoLock(const SString &prov
return NULL;
}
-EventPipeSessionProvider* EventPipeConfiguration::GetSessionProvider(EventPipeSession *pSession, EventPipeProvider *pProvider)
+EventPipeSessionProvider *EventPipeConfiguration::GetSessionProvider(EventPipeSession *pSession, EventPipeProvider *pProvider)
{
CONTRACTL
{
@@ -291,9 +289,9 @@ EventPipeSessionProvider* EventPipeConfiguration::GetSessionProvider(EventPipeSe
CONTRACTL_END;
EventPipeSessionProvider *pRet = NULL;
- if(pSession != NULL)
+ if (pSession != NULL)
{
- pRet = pSession->GetSessionProvider(pProvider);
+ pRet = pSession->GetSessionProvider(pProvider);
}
return pRet;
}
@@ -303,24 +301,35 @@ size_t EventPipeConfiguration::GetCircularBufferSize() const
LIMITED_METHOD_CONTRACT;
size_t ret = 0;
- if(m_pSession != NULL)
+ if (m_pSession != NULL)
{
ret = m_pSession->GetCircularBufferSize();
}
return ret;
}
-EventPipeSession* EventPipeConfiguration::CreateSession(EventPipeSessionType sessionType, unsigned int circularBufferSizeInMB, EventPipeProviderConfiguration *pProviders, unsigned int numProviders, UINT64 multiFileTraceLengthInSeconds)
+EventPipeSession *EventPipeConfiguration::CreateSession(
+ EventPipeSessionType sessionType,
+ unsigned int circularBufferSizeInMB,
+ const EventPipeProviderConfiguration *pProviders,
+ uint32_t numProviders,
+ uint64_t multiFileTraceLengthInSeconds)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
+ PRECONDITION((numProviders == 0) || (numProviders > 0 && pProviders != nullptr));
}
CONTRACTL_END;
- return new EventPipeSession(sessionType, circularBufferSizeInMB, pProviders, numProviders, multiFileTraceLengthInSeconds);
+ return new EventPipeSession(
+ sessionType,
+ circularBufferSizeInMB,
+ pProviders,
+ numProviders,
+ multiFileTraceLengthInSeconds);
}
void EventPipeConfiguration::DeleteSession(EventPipeSession *pSession)
@@ -336,9 +345,9 @@ void EventPipeConfiguration::DeleteSession(EventPipeSession *pSession)
CONTRACTL_END;
// TODO: Multiple session support will require individual enabled bits.
- if(pSession != NULL && !m_enabled)
+ if (pSession != NULL && !m_enabled)
{
- delete(pSession);
+ delete (pSession);
}
}
@@ -361,14 +370,14 @@ void EventPipeConfiguration::Enable(EventPipeSession *pSession)
// The provider list should be non-NULL, but can be NULL on shutdown.
if (m_pProviderList != NULL)
{
- SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
- while(pElem != NULL)
+ SListElem<EventPipeProvider *> *pElem = m_pProviderList->GetHead();
+ while (pElem != NULL)
{
EventPipeProvider *pProvider = pElem->GetValue();
// Enable the provider if it has been configured.
EventPipeSessionProvider *pSessionProvider = GetSessionProvider(m_pSession, pProvider);
- if(pSessionProvider != NULL)
+ if (pSessionProvider != NULL)
{
pProvider->SetConfiguration(
true /* providerEnabled */,
@@ -400,8 +409,8 @@ void EventPipeConfiguration::Disable(EventPipeSession *pSession)
// The provider list should be non-NULL, but can be NULL on shutdown.
if (m_pProviderList != NULL)
{
- SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
- while(pElem != NULL)
+ SListElem<EventPipeProvider *> *pElem = m_pProviderList->GetHead();
+ while (pElem != NULL)
{
EventPipeProvider *pProvider = pElem->GetValue();
pProvider->SetConfiguration(
@@ -458,7 +467,7 @@ void EventPipeConfiguration::EnableRundown(EventPipeSession *pSession)
Enable(pSession);
}
-EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance, unsigned int metadataId)
+EventPipeEventInstance *EventPipeConfiguration::BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance, unsigned int metadataId)
{
CONTRACTL
{
@@ -490,7 +499,7 @@ EventPipeEventInstance* EventPipeConfiguration::BuildEventMetadataEvent(EventPip
memcpy(currentPtr, &metadataId, sizeof(metadataId));
currentPtr += sizeof(metadataId);
- memcpy(currentPtr, (BYTE*)providerName.GetUnicode(), providerNameLength);
+ memcpy(currentPtr, (BYTE *)providerName.GetUnicode(), providerNameLength);
currentPtr += providerNameLength;
// Write the incoming payload data.
@@ -523,19 +532,18 @@ void EventPipeConfiguration::DeleteDeferredProviders()
MODE_ANY;
// Lock must be held by EventPipe::Disable.
PRECONDITION(EventPipe::GetLock()->OwnedByCurrentThread());
-
}
CONTRACTL_END;
// The provider list should be non-NULL, but can be NULL on shutdown.
if (m_pProviderList != NULL)
{
- SListElem<EventPipeProvider*> *pElem = m_pProviderList->GetHead();
- while(pElem != NULL)
+ SListElem<EventPipeProvider *> *pElem = m_pProviderList->GetHead();
+ while (pElem != NULL)
{
EventPipeProvider *pProvider = pElem->GetValue();
pElem = m_pProviderList->GetNext(pElem);
- if(pProvider->GetDeleteDeferred())
+ if (pProvider->GetDeleteDeferred())
{
DeleteProvider(pProvider);
}
diff --git a/src/vm/eventpipeconfiguration.h b/src/vm/eventpipeconfiguration.h
index 5eec76ca80..6021305b82 100644
--- a/src/vm/eventpipeconfiguration.h
+++ b/src/vm/eventpipeconfiguration.h
@@ -13,10 +13,8 @@ class EventPipeSessionProvider;
class EventPipeEvent;
class EventPipeEventInstance;
class EventPipeProvider;
-struct EventPipeProviderConfiguration;
class EventPipeSession;
enum class EventPipeSessionType;
-class EventPipeSessionProvider;
enum class EventPipeEventLevel
{
@@ -31,7 +29,6 @@ enum class EventPipeEventLevel
class EventPipeConfiguration
{
public:
-
EventPipeConfiguration();
~EventPipeConfiguration();
@@ -39,7 +36,7 @@ public:
void Initialize();
// Create a new provider.
- EventPipeProvider* CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData);
+ EventPipeProvider *CreateProvider(const SString &providerName, EventPipeCallback pCallbackFunction, void *pCallbackData);
// Delete a provider.
void DeleteProvider(EventPipeProvider *pProvider);
@@ -51,10 +48,15 @@ public:
bool UnregisterProvider(EventPipeProvider &provider);
// Get the provider with the specified provider ID if it exists.
- EventPipeProvider* GetProvider(const SString &providerID);
+ EventPipeProvider *GetProvider(const SString &providerID);
// Create a new session.
- EventPipeSession* CreateSession(EventPipeSessionType sessionType, unsigned int circularBufferSizeInMB, EventPipeProviderConfiguration *pProviders, unsigned int numProviders, UINT64 multiFileTraceLengthInSeconds = 0);
+ EventPipeSession *CreateSession(
+ EventPipeSessionType sessionType,
+ unsigned int circularBufferSizeInMB,
+ const EventPipeProviderConfiguration *pProviders,
+ uint32_t numProviders,
+ uint64_t multiFileTraceLengthInSeconds = 0);
// Delete a session.
void DeleteSession(EventPipeSession *pSession);
@@ -78,7 +80,7 @@ public:
void EnableRundown(EventPipeSession *pSession);
// Get the event used to write metadata to the event stream.
- EventPipeEventInstance* BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance, unsigned int metdataId);
+ EventPipeEventInstance *BuildEventMetadataEvent(EventPipeEventInstance &sourceInstance, unsigned int metdataId);
// Delete deferred providers.
void DeleteDeferredProviders();
@@ -93,12 +95,11 @@ public:
}
private:
-
// Get the provider without taking the lock.
- EventPipeProvider* GetProviderNoLock(const SString &providerID);
+ EventPipeProvider *GetProviderNoLock(const SString &providerID);
// Get the enabled provider.
- EventPipeSessionProvider* GetSessionProvider(EventPipeSession *pSession, EventPipeProvider *pProvider);
+ EventPipeSessionProvider *GetSessionProvider(EventPipeSession *pSession, EventPipeProvider *pProvider);
// The one and only EventPipe session.
EventPipeSession *m_pSession;
@@ -107,7 +108,7 @@ private:
Volatile<bool> m_enabled;
// The list of event pipe providers.
- SList<SListElem<EventPipeProvider*>> *m_pProviderList;
+ SList<SListElem<EventPipeProvider *>> *m_pProviderList;
// The provider used to write configuration events to the event stream.
EventPipeProvider *m_pConfigProvider;
@@ -117,7 +118,7 @@ private:
// The provider name for the configuration event pipe provider.
// This provider is used to emit configuration events.
- const static WCHAR* s_configurationProviderName;
+ const static WCHAR *s_configurationProviderName;
// True if rundown is enabled.
Volatile<bool> m_rundownEnabled;
diff --git a/src/vm/eventpipeeventinstance.h b/src/vm/eventpipeeventinstance.h
index 80fd49d946..cf995f2534 100644
--- a/src/vm/eventpipeeventinstance.h
+++ b/src/vm/eventpipeeventinstance.h
@@ -14,6 +14,8 @@
#include "fastserializableobject.h"
#include "fastserializer.h"
+class EventPipeJsonFile;
+
class EventPipeEventInstance
{
// Declare friends.
diff --git a/src/vm/eventpipeeventsource.cpp b/src/vm/eventpipeeventsource.cpp
index 20dc5b2686..ca62677cde 100644
--- a/src/vm/eventpipeeventsource.cpp
+++ b/src/vm/eventpipeeventsource.cpp
@@ -9,6 +9,7 @@
#include "eventpipemetadatagenerator.h"
#include "eventpipeprovider.h"
#include "eventpipesession.h"
+#include "eventpipesessionprovider.h"
#ifdef FEATURE_PERFTRACING
@@ -50,6 +51,7 @@ EventPipeEventSource::EventPipeEventSource()
0, /* keywords */
0, /* eventVersion */
EventPipeEventLevel::LogAlways,
+ false, /* needStack */
pMetadata,
(unsigned int)metadataLength);
diff --git a/src/vm/eventpipefile.cpp b/src/vm/eventpipefile.cpp
index 424bbef5ad..2e98fd5ad7 100644
--- a/src/vm/eventpipefile.cpp
+++ b/src/vm/eventpipefile.cpp
@@ -11,8 +11,7 @@
#ifdef FEATURE_PERFTRACING
-EventPipeFile::EventPipeFile(
- SString &outputFilePath)
+EventPipeFile::EventPipeFile(SString &outputFilePath)
{
CONTRACTL
{
@@ -43,7 +42,7 @@ EventPipeFile::EventPipeFile(
m_samplingRateInNs = SampleProfiler::GetSamplingRate();
// Create the file stream and write the header.
- m_pSerializer = new FastSerializer(outputFilePath);
+ m_pSerializer = new FastSerializer(new FileStreamWriter(outputFilePath));
m_serializationLock.Init(LOCK_TYPE_DEFAULT);
m_pMetadataIds = new MapSHashWithRemove<EventPipeEvent*, unsigned int>();
@@ -101,7 +100,7 @@ void EventPipeFile::WriteEvent(EventPipeEventInstance &instance)
metadataId = GenerateMetadataId();
EventPipeEventInstance* pMetadataInstance = EventPipe::GetConfiguration()->BuildEventMetadataEvent(instance, metadataId);
-
+
WriteToBlock(*pMetadataInstance, 0); // 0 breaks recursion and represents the metadata event.
SaveMetadataId(*instance.GetEvent(), metadataId);
@@ -129,7 +128,7 @@ void EventPipeFile::WriteEnd()
// "After the last EventBlock is emitted, the stream is ended by emitting a NullReference Tag which indicates that there are no more objects in the stream to read."
// see https://github.com/Microsoft/perfview/blob/master/src/TraceEvent/EventPipe/EventPipeFormat.md for more
- m_pSerializer->WriteTag(FastSerializerTags::NullReference);
+ m_pSerializer->WriteTag(FastSerializerTags::NullReference);
}
void EventPipeFile::WriteToBlock(EventPipeEventInstance &instance, unsigned int metadataId)
diff --git a/src/vm/eventpipeinternal.cpp b/src/vm/eventpipeinternal.cpp
new file mode 100644
index 0000000000..30a14642c0
--- /dev/null
+++ b/src/vm/eventpipeinternal.cpp
@@ -0,0 +1,270 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "common.h"
+#include "eventpipe.h"
+#include "eventpipeconfiguration.h"
+#include "eventpipeeventinstance.h"
+#include "eventpipeinternal.h"
+#include "eventpipeprovider.h"
+#include "eventpipesession.h"
+#include "eventpipesessionprovider.h"
+
+#ifdef FEATURE_PAL
+#include "pal.h"
+#endif // FEATURE_PAL
+
+#ifdef FEATURE_PERFTRACING
+
+UINT64 QCALLTYPE EventPipeInternal::Enable(
+ __in_z LPCWSTR outputFile,
+ UINT32 circularBufferSizeInMB,
+ INT64 profilerSamplingRateInNanoseconds,
+ EventPipeProviderConfiguration *pProviders,
+ UINT32 numProviders,
+ UINT64 multiFileTraceLengthInSeconds)
+{
+ QCALL_CONTRACT;
+
+ UINT64 sessionID = 0;
+
+ BEGIN_QCALL;
+ {
+ sessionID = EventPipe::Enable(
+ outputFile,
+ circularBufferSizeInMB,
+ profilerSamplingRateInNanoseconds,
+ pProviders,
+ numProviders,
+ multiFileTraceLengthInSeconds);
+ }
+ END_QCALL;
+
+ return sessionID;
+}
+
+void QCALLTYPE EventPipeInternal::Disable(UINT64 sessionID)
+{
+ QCALL_CONTRACT;
+
+ BEGIN_QCALL;
+ EventPipe::Disable(sessionID);
+ END_QCALL;
+}
+
+bool QCALLTYPE EventPipeInternal::GetSessionInfo(UINT64 sessionID, EventPipeSessionInfo *pSessionInfo)
+{
+ QCALL_CONTRACT;
+
+ bool retVal = false;
+ BEGIN_QCALL;
+
+ if (pSessionInfo != NULL)
+ {
+ EventPipeSession *pSession = EventPipe::GetSession(sessionID);
+ if (pSession != NULL)
+ {
+ pSessionInfo->StartTimeAsUTCFileTime = pSession->GetStartTime();
+ pSessionInfo->StartTimeStamp.QuadPart = pSession->GetStartTimeStamp().QuadPart;
+ QueryPerformanceFrequency(&pSessionInfo->TimeStampFrequency);
+ retVal = true;
+ }
+ }
+
+ END_QCALL;
+ return retVal;
+}
+
+INT_PTR QCALLTYPE EventPipeInternal::CreateProvider(
+ __in_z LPCWSTR providerName,
+ EventPipeCallback pCallbackFunc)
+{
+ QCALL_CONTRACT;
+
+ EventPipeProvider *pProvider = NULL;
+
+ BEGIN_QCALL;
+
+ pProvider = EventPipe::CreateProvider(providerName, pCallbackFunc, NULL);
+
+ END_QCALL;
+
+ return reinterpret_cast<INT_PTR>(pProvider);
+}
+
+INT_PTR QCALLTYPE EventPipeInternal::DefineEvent(
+ INT_PTR provHandle,
+ UINT32 eventID,
+ __int64 keywords,
+ UINT32 eventVersion,
+ UINT32 level,
+ void *pMetadata,
+ UINT32 metadataLength)
+{
+ QCALL_CONTRACT;
+
+ EventPipeEvent *pEvent = NULL;
+
+ BEGIN_QCALL;
+
+ _ASSERTE(provHandle != NULL);
+ EventPipeProvider *pProvider = reinterpret_cast<EventPipeProvider *>(provHandle);
+ pEvent = pProvider->AddEvent(eventID, keywords, eventVersion, (EventPipeEventLevel)level, /* needStack = */ true, (BYTE *)pMetadata, metadataLength);
+ _ASSERTE(pEvent != NULL);
+
+ END_QCALL;
+
+ return reinterpret_cast<INT_PTR>(pEvent);
+}
+
+INT_PTR QCALLTYPE EventPipeInternal::GetProvider(__in_z LPCWSTR providerName)
+{
+ QCALL_CONTRACT;
+
+ EventPipeProvider *pProvider = NULL;
+
+ BEGIN_QCALL;
+
+ pProvider = EventPipe::GetProvider(providerName);
+
+ END_QCALL;
+
+ return reinterpret_cast<INT_PTR>(pProvider);
+}
+
+void QCALLTYPE EventPipeInternal::DeleteProvider(INT_PTR provHandle)
+{
+ QCALL_CONTRACT;
+ BEGIN_QCALL;
+
+ if (provHandle != NULL)
+ {
+ EventPipeProvider *pProvider = reinterpret_cast<EventPipeProvider *>(provHandle);
+ EventPipe::DeleteProvider(pProvider);
+ }
+
+ END_QCALL;
+}
+
+int QCALLTYPE EventPipeInternal::EventActivityIdControl(uint32_t controlCode, GUID *pActivityId)
+{
+
+ QCALL_CONTRACT;
+
+ int retVal = 0;
+
+ BEGIN_QCALL;
+
+ Thread *pThread = GetThread();
+ if (pThread == NULL || pActivityId == NULL)
+ {
+ retVal = 1;
+ }
+ else
+ {
+ ActivityControlCode activityControlCode = (ActivityControlCode)controlCode;
+ GUID currentActivityId;
+ switch (activityControlCode)
+ {
+ case ActivityControlCode::EVENT_ACTIVITY_CONTROL_GET_ID:
+
+ *pActivityId = *pThread->GetActivityId();
+ break;
+
+ case ActivityControlCode::EVENT_ACTIVITY_CONTROL_SET_ID:
+
+ pThread->SetActivityId(pActivityId);
+ break;
+
+ case ActivityControlCode::EVENT_ACTIVITY_CONTROL_CREATE_ID:
+
+ CoCreateGuid(pActivityId);
+ break;
+
+ case ActivityControlCode::EVENT_ACTIVITY_CONTROL_GET_SET_ID:
+
+ currentActivityId = *pThread->GetActivityId();
+ pThread->SetActivityId(pActivityId);
+ *pActivityId = currentActivityId;
+ break;
+
+ case ActivityControlCode::EVENT_ACTIVITY_CONTROL_CREATE_SET_ID:
+
+ *pActivityId = *pThread->GetActivityId();
+ CoCreateGuid(&currentActivityId);
+ pThread->SetActivityId(&currentActivityId);
+ break;
+
+ default:
+ retVal = 1;
+ }
+ }
+
+ END_QCALL;
+ return retVal;
+}
+
+void QCALLTYPE EventPipeInternal::WriteEvent(
+ INT_PTR eventHandle,
+ UINT32 eventID,
+ void *pData,
+ UINT32 length,
+ LPCGUID pActivityId,
+ LPCGUID pRelatedActivityId)
+{
+ QCALL_CONTRACT;
+ BEGIN_QCALL;
+
+ _ASSERTE(eventHandle != NULL);
+ EventPipeEvent *pEvent = reinterpret_cast<EventPipeEvent *>(eventHandle);
+ EventPipe::WriteEvent(*pEvent, (BYTE *)pData, length, pActivityId, pRelatedActivityId);
+
+ END_QCALL;
+}
+
+void QCALLTYPE EventPipeInternal::WriteEventData(
+ INT_PTR eventHandle,
+ UINT32 eventID,
+ EventData *pEventData,
+ UINT32 eventDataCount,
+ LPCGUID pActivityId,
+ LPCGUID pRelatedActivityId)
+{
+ QCALL_CONTRACT;
+ BEGIN_QCALL;
+
+ _ASSERTE(eventHandle != NULL);
+ EventPipeEvent *pEvent = reinterpret_cast<EventPipeEvent *>(eventHandle);
+ EventPipe::WriteEvent(*pEvent, pEventData, eventDataCount, pActivityId, pRelatedActivityId);
+
+ END_QCALL;
+}
+
+bool QCALLTYPE EventPipeInternal::GetNextEvent(EventPipeEventInstanceData *pInstance)
+{
+ QCALL_CONTRACT;
+
+ EventPipeEventInstance *pNextInstance = NULL;
+ BEGIN_QCALL;
+
+ _ASSERTE(pInstance != NULL);
+
+ pNextInstance = EventPipe::GetNextEvent();
+ if (pNextInstance)
+ {
+ pInstance->ProviderID = pNextInstance->GetEvent()->GetProvider();
+ pInstance->EventID = pNextInstance->GetEvent()->GetEventID();
+ pInstance->ThreadID = pNextInstance->GetThreadId();
+ pInstance->TimeStamp.QuadPart = pNextInstance->GetTimeStamp()->QuadPart;
+ pInstance->ActivityId = *pNextInstance->GetActivityId();
+ pInstance->RelatedActivityId = *pNextInstance->GetRelatedActivityId();
+ pInstance->Payload = pNextInstance->GetData();
+ pInstance->PayloadLength = pNextInstance->GetDataLength();
+ }
+
+ END_QCALL;
+ return pNextInstance != NULL;
+}
+
+#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipeinternal.h b/src/vm/eventpipeinternal.h
new file mode 100644
index 0000000000..bbd4ad633b
--- /dev/null
+++ b/src/vm/eventpipeinternal.h
@@ -0,0 +1,107 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __EVENTPIPEINTERNAL_H__
+#define __EVENTPIPEINTERNAL_H__
+
+#ifdef FEATURE_PERFTRACING
+
+// TODO: Maybe we should move the other types that are used on PInvoke here?
+
+class EventPipeInternal
+{
+private:
+ enum class ActivityControlCode
+ {
+ EVENT_ACTIVITY_CONTROL_GET_ID = 1,
+ EVENT_ACTIVITY_CONTROL_SET_ID = 2,
+ EVENT_ACTIVITY_CONTROL_CREATE_ID = 3,
+ EVENT_ACTIVITY_CONTROL_GET_SET_ID = 4,
+ EVENT_ACTIVITY_CONTROL_CREATE_SET_ID = 5
+ };
+
+ struct EventPipeEventInstanceData
+ {
+ void *ProviderID;
+ unsigned int EventID;
+ unsigned int ThreadID;
+ LARGE_INTEGER TimeStamp;
+ GUID ActivityId;
+ GUID RelatedActivityId;
+ const BYTE *Payload;
+ unsigned int PayloadLength;
+ };
+
+ struct EventPipeSessionInfo
+ {
+ FILETIME StartTimeAsUTCFileTime;
+ LARGE_INTEGER StartTimeStamp;
+ LARGE_INTEGER TimeStampFrequency;
+ };
+
+public:
+ //!
+ //! Sets the sampling rate and enables the event pipe for the specified configuration.
+ //!
+ static UINT64 QCALLTYPE Enable(
+ __in_z LPCWSTR outputFile,
+ UINT32 circularBufferSizeInMB,
+ INT64 profilerSamplingRateInNanoseconds,
+ EventPipeProviderConfiguration *pProviders,
+ UINT32 numProviders,
+ UINT64 multiFileTraceLengthInSeconds);
+
+ //! TODO: Add a ListActiveSessions to get the live SessionID in order to Disable?
+
+ //!
+ //! Disables the specified session Id.
+ //!
+ static void QCALLTYPE Disable(UINT64 sessionID);
+
+ static bool QCALLTYPE GetSessionInfo(UINT64 sessionID, EventPipeSessionInfo *pSessionInfo);
+
+ static INT_PTR QCALLTYPE CreateProvider(
+ __in_z LPCWSTR providerName,
+ EventPipeCallback pCallbackFunc);
+
+ static INT_PTR QCALLTYPE DefineEvent(
+ INT_PTR provHandle,
+ UINT32 eventID,
+ __int64 keywords,
+ UINT32 eventVersion,
+ UINT32 level,
+ void *pMetadata,
+ UINT32 metadataLength);
+
+ static INT_PTR QCALLTYPE GetProvider(
+ __in_z LPCWSTR providerName);
+
+ static void QCALLTYPE DeleteProvider(
+ INT_PTR provHandle);
+
+ static int QCALLTYPE EventActivityIdControl(
+ uint32_t controlCode,
+ GUID *pActivityId);
+
+ static void QCALLTYPE WriteEvent(
+ INT_PTR eventHandle,
+ UINT32 eventID,
+ void *pData,
+ UINT32 length,
+ LPCGUID pActivityId, LPCGUID pRelatedActivityId);
+
+ static void QCALLTYPE WriteEventData(
+ INT_PTR eventHandle,
+ UINT32 eventID,
+ EventData *pEventData,
+ UINT32 eventDataCount,
+ LPCGUID pActivityId, LPCGUID pRelatedActivityId);
+
+ static bool QCALLTYPE GetNextEvent(
+ EventPipeEventInstanceData *pInstance);
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __EVENTPIPEINTERNAL_H__
diff --git a/src/vm/eventpipeprotocolhelper.cpp b/src/vm/eventpipeprotocolhelper.cpp
new file mode 100644
index 0000000000..bfcdcfa2f8
--- /dev/null
+++ b/src/vm/eventpipeprotocolhelper.cpp
@@ -0,0 +1,156 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "common.h"
+#include "eventpipeprotocolhelper.h"
+#include "diagnosticsipc.h"
+#include "diagnosticsprotocol.h"
+
+#ifdef FEATURE_PERFTRACING
+
+bool EventPipeProtocolHelper::TryParseProviderConfigurations(uint8_t *&bufferCursor, uint32_t &bufferLen, CQuickArray<EventPipeProviderConfiguration> &result)
+{
+ // Picking an arbitrary upper bound,
+ // This should be larger than any reasonable client request.
+ const uint32_t MaxCountConfigs = 1000; // TODO: This might be too large.
+
+ uint32_t countConfigs = 0;
+ if (!TryParse(bufferCursor, bufferLen, countConfigs))
+ return false;
+ if (countConfigs > MaxCountConfigs)
+ return false;
+ EventPipeProviderConfiguration *pConfigs = result.AllocNoThrow(countConfigs);
+ if (pConfigs == nullptr)
+ return false;
+
+ for (uint32_t i = 0; i < countConfigs; i++)
+ {
+ uint64_t keywords = 0;
+ if (!TryParse(bufferCursor, bufferLen, keywords))
+ return false;
+
+ uint32_t logLevel = 0;
+ if (!TryParse(bufferCursor, bufferLen, logLevel))
+ return false;
+ if (logLevel > 5) // (logLevel > EventPipeEventLevel::Verbose)
+ return false;
+
+ LPCWSTR pProviderName = nullptr;
+ if (!TryParseString(bufferCursor, bufferLen, pProviderName))
+ return false;
+ if (wcslen(pProviderName) == 0)
+ return false; // TODO: Should we ignore these input?
+
+ LPCWSTR pFilterData = nullptr; // This parameter is optional.
+ TryParseString(bufferCursor, bufferLen, pFilterData);
+
+ pConfigs[i] = EventPipeProviderConfiguration(pProviderName, keywords, logLevel, pFilterData);
+ }
+ return true;
+}
+
+void EventPipeProtocolHelper::EnableFileTracingEventHandler(IpcStream *pStream)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(pStream != nullptr);
+ }
+ CONTRACTL_END;
+
+ // TODO: Read within a loop.
+ const uint32_t BufferSize = 8192;
+ uint8_t buffer[BufferSize]{};
+ uint32_t nNumberOfBytesRead = 0;
+ bool fSuccess = pStream->Read(buffer, sizeof(buffer), nNumberOfBytesRead);
+ if (!fSuccess)
+ {
+ // TODO: Add error handling.
+ delete pStream;
+ return;
+ }
+
+ // The protocol buffer is defined as:
+ // X, Y, Z means encode bytes for X followed by bytes for Y followed by bytes for Z
+ // message = uint circularBufferMB, ulong multiFileTraceLength, string outputPath, array<provider_config> providers
+ // uint = 4 little endian bytes
+ // ulong = 8 little endian bytes
+ // wchar = 2 little endian bytes, UTF16 encoding
+ // array<T> = uint length, length # of Ts
+ // string = (array<char> where the last char must = 0) or (length = 0)
+ // provider_config = ulong keywords, uint logLevel, string provider_name, string filter_data
+
+ LPCWSTR strOutputPath;
+ uint32_t circularBufferSizeInMB = EventPipeProtocolHelper::DefaultCircularBufferMB;
+ uint64_t multiFileTraceLengthInSeconds = EventPipeProtocolHelper::DefaultMultiFileTraceLengthInSeconds;
+ CQuickArray<EventPipeProviderConfiguration> providerConfigs;
+
+ uint8_t *pBufferCursor = buffer;
+ uint32_t bufferLen = nNumberOfBytesRead;
+ if (!TryParse(pBufferCursor, bufferLen, circularBufferSizeInMB) ||
+ !TryParse(pBufferCursor, bufferLen, multiFileTraceLengthInSeconds) ||
+ !TryParseString(pBufferCursor, bufferLen, strOutputPath) ||
+ !TryParseProviderConfigurations(pBufferCursor, bufferLen, providerConfigs))
+ {
+ return; // TODO: error handling
+ }
+
+ EventPipeSessionID sessionId = (EventPipeSessionID) nullptr;
+ if (providerConfigs.Size() > 0)
+ {
+ sessionId = EventPipe::Enable(
+ strOutputPath, // outputFile
+ circularBufferSizeInMB, // circularBufferSizeInMB
+ DefaultProfilerSamplingRateInNanoseconds, // ProfilerSamplingRateInNanoseconds
+ providerConfigs.Ptr(), // pConfigs
+ static_cast<uint32_t>(providerConfigs.Size()), // numConfigs
+ multiFileTraceLengthInSeconds); // multiFileTraceLengthInSeconds
+ }
+
+ uint32_t nBytesWritten = 0;
+ fSuccess = pStream->Write(&sessionId, sizeof(sessionId), nBytesWritten);
+ if (!fSuccess)
+ {
+ // TODO: Add error handling.
+ delete pStream;
+ return;
+ }
+
+ fSuccess = pStream->Flush();
+ if (!fSuccess)
+ {
+ // TODO: Add error handling.
+ }
+ delete pStream;
+}
+
+void EventPipeProtocolHelper::DisableTracingEventHandler(IpcStream *pStream)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(pStream != nullptr);
+ }
+ CONTRACTL_END;
+
+ uint32_t nNumberOfBytesRead = 0;
+ EventPipeSessionID sessionId = (EventPipeSessionID) nullptr;
+ const bool fSuccess = pStream->Read(&sessionId, sizeof(sessionId), nNumberOfBytesRead);
+ if (!fSuccess || nNumberOfBytesRead != sizeof(sessionId))
+ {
+ // TODO: Add error handling.
+ delete pStream;
+ return;
+ }
+
+ EventPipe::Disable(sessionId);
+ // TODO: Should we acknowledge back?
+ delete pStream;
+}
+
+#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipeprotocolhelper.h b/src/vm/eventpipeprotocolhelper.h
new file mode 100644
index 0000000000..fb3f65a247
--- /dev/null
+++ b/src/vm/eventpipeprotocolhelper.h
@@ -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.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __EVENTPIPE_PROTOCOL_HELPER_H__
+#define __EVENTPIPE_PROTOCOL_HELPER_H__
+
+#ifdef FEATURE_PERFTRACING
+
+#include "common.h"
+#include "eventpipe.h"
+
+class IpcStream;
+
+class EventPipeProtocolHelper
+{
+public:
+ // IPC event handlers.
+ static void EnableFileTracingEventHandler(IpcStream *pStream);
+ static void DisableTracingEventHandler(IpcStream *pStream);
+
+private:
+ const static uint32_t DefaultCircularBufferMB = 1024; // 1 GB
+ const static uint64_t DefaultMultiFileTraceLengthInSeconds = 0;
+ const static uint32_t DefaultProfilerSamplingRateInNanoseconds = 1000000;
+
+ //! Read a list of providers: "Provider[,Provider]"
+ //! Provider: "(GUID|KnownProviderName)[:Flags[:Level][:KeyValueArgs]]"
+ //! KeyValueArgs: "[key1=value1][;key2=value2]"
+ static bool TryParseProviderConfigurations(uint8_t *&bufferCursor, uint32_t &bufferLen, CQuickArray<EventPipeProviderConfiguration> &result);
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __EVENTPIPE_PROTOCOL_HELPER_H__
diff --git a/src/vm/eventpipeprovider.cpp b/src/vm/eventpipeprovider.cpp
index 4630c93acd..4c6eabcf50 100644
--- a/src/vm/eventpipeprovider.cpp
+++ b/src/vm/eventpipeprovider.cpp
@@ -130,19 +130,6 @@ void EventPipeProvider::SetConfiguration(bool providerEnabled, INT64 keywords, E
InvokeCallback(pFilterData);
}
-EventPipeEvent* EventPipeProvider::AddEvent(unsigned int eventID, INT64 keywords, unsigned int eventVersion, EventPipeEventLevel level, BYTE *pMetadata, unsigned int metadataLength)
-{
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- return AddEvent(eventID, keywords, eventVersion, level, true /* needStack */, pMetadata, metadataLength);
-}
-
EventPipeEvent* EventPipeProvider::AddEvent(unsigned int eventID, INT64 keywords, unsigned int eventVersion, EventPipeEventLevel level, bool needStack, BYTE *pMetadata, unsigned int metadataLength)
{
CONTRACTL
diff --git a/src/vm/eventpipeprovider.h b/src/vm/eventpipeprovider.h
index dffdc7e3db..8ae57d9d62 100644
--- a/src/vm/eventpipeprovider.h
+++ b/src/vm/eventpipeprovider.h
@@ -70,17 +70,10 @@ public:
bool EventEnabled(INT64 keywords, EventPipeEventLevel eventLevel) const;
// Create a new event.
- EventPipeEvent* AddEvent(unsigned int eventID, INT64 keywords, unsigned int eventVersion, EventPipeEventLevel level, BYTE *pMetadata = NULL, unsigned int metadataLength = 0);
-
- private:
-
- // Create a new event, but allow needStack to be specified.
- // In general, we want stack walking to be controlled by the consumer and not the producer of events.
- // However, there are a couple of cases that we know we don't want to do a stackwalk that would affect performance significantly:
- // 1. Sample profiler events: The sample profiler already does a stack walk of the target thread. Doing one of the sampler thread is a waste.
- // 2. Metadata events: These aren't as painful but because we have to keep this functionality around, might as well use it.
EventPipeEvent* AddEvent(unsigned int eventID, INT64 keywords, unsigned int eventVersion, EventPipeEventLevel level, bool needStack, BYTE *pMetadata = NULL, unsigned int metadataLength = 0);
+private:
+
// Add an event to the provider.
void AddEvent(EventPipeEvent &event);
diff --git a/src/vm/eventpipesession.cpp b/src/vm/eventpipesession.cpp
index 7388472c7d..03bc6c681a 100644
--- a/src/vm/eventpipesession.cpp
+++ b/src/vm/eventpipesession.cpp
@@ -6,30 +6,30 @@
#include "eventpipe.h"
#include "eventpipeprovider.h"
#include "eventpipesession.h"
+#include "eventpipesessionprovider.h"
#ifdef FEATURE_PERFTRACING
EventPipeSession::EventPipeSession(
EventPipeSessionType sessionType,
unsigned int circularBufferSizeInMB,
- EventPipeProviderConfiguration *pProviders,
- unsigned int numProviders,
- UINT64 multiFileTraceLengthInSeconds)
+ const EventPipeProviderConfiguration *pProviders,
+ uint32_t numProviders,
+ uint64_t multiFileTraceLengthInSeconds)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
+ PRECONDITION((numProviders == 0) || (numProviders > 0 && pProviders != nullptr));
}
CONTRACTL_END;
m_sessionType = sessionType;
m_circularBufferSizeInBytes = circularBufferSizeInMB * 1024 * 1024; // 1MB;
m_rundownEnabled = false;
- m_pProviderList = new EventPipeSessionProviderList(
- pProviders,
- numProviders);
+ m_pProviderList = new EventPipeSessionProviderList(pProviders, numProviders);
m_multiFileTraceLengthInSeconds = multiFileTraceLengthInSeconds;
GetSystemTimeAsFileTime(&m_sessionStartTime);
QueryPerformanceCounter(&m_sessionStartTimeStamp);
@@ -90,218 +90,4 @@ EventPipeSessionProvider* EventPipeSession::GetSessionProvider(EventPipeProvider
return m_pProviderList->GetSessionProvider(pProvider);
}
-EventPipeSessionProviderList::EventPipeSessionProviderList(
- EventPipeProviderConfiguration *pConfigs,
- unsigned int numConfigs)
-{
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- m_pProviders = new SList<SListElem<EventPipeSessionProvider*>>();
- m_pCatchAllProvider = NULL;
- for(unsigned int i=0; i<numConfigs; i++)
- {
- EventPipeProviderConfiguration *pConfig = &pConfigs[i];
-
- // Enable all events if the provider name == '*', all keywords are on and the requested level == verbose.
- if((wcscmp(W("*"), pConfig->GetProviderName()) == 0) && (pConfig->GetKeywords() == 0xFFFFFFFFFFFFFFFF) && ((EventPipeEventLevel)pConfig->GetLevel() == EventPipeEventLevel::Verbose) && (m_pCatchAllProvider == NULL))
- {
- m_pCatchAllProvider = new EventPipeSessionProvider(NULL, 0xFFFFFFFFFFFFFFFF, EventPipeEventLevel::Verbose, NULL);
- }
- else
- {
- EventPipeSessionProvider *pProvider = new EventPipeSessionProvider(
- pConfig->GetProviderName(),
- pConfig->GetKeywords(),
- (EventPipeEventLevel)pConfig->GetLevel(),
- pConfig->GetFilterData());
-
- m_pProviders->InsertTail(new SListElem<EventPipeSessionProvider*>(pProvider));
- }
- }
-}
-
-EventPipeSessionProviderList::~EventPipeSessionProviderList()
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- if(m_pProviders != NULL)
- {
- SListElem<EventPipeSessionProvider*> *pElem = m_pProviders->GetHead();
- while(pElem != NULL)
- {
- EventPipeSessionProvider *pProvider = pElem->GetValue();
- delete pProvider;
-
- SListElem<EventPipeSessionProvider*> *pCurElem = pElem;
- pElem = m_pProviders->GetNext(pElem);
- delete pCurElem;
- }
-
- delete m_pProviders;
- m_pProviders = NULL;
- }
- if(m_pCatchAllProvider != NULL)
- {
- delete(m_pCatchAllProvider);
- m_pCatchAllProvider = NULL;
- }
-}
-
-void EventPipeSessionProviderList::AddSessionProvider(EventPipeSessionProvider *pProvider)
-{
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- if(pProvider != NULL)
- {
- m_pProviders->InsertTail(new SListElem<EventPipeSessionProvider*>(pProvider));
- }
-}
-
-EventPipeSessionProvider* EventPipeSessionProviderList::GetSessionProvider(
- EventPipeProvider *pProvider)
-{
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- // Exists when tracing was enabled at start-up and all events were requested. This is a diagnostic config.
- if(m_pCatchAllProvider != NULL)
- {
- return m_pCatchAllProvider;
- }
-
- if(m_pProviders == NULL)
- {
- return NULL;
- }
-
- SString providerNameStr = pProvider->GetProviderName();
- LPCWSTR providerName = providerNameStr.GetUnicode();
-
- EventPipeSessionProvider *pSessionProvider = NULL;
- SListElem<EventPipeSessionProvider*> *pElem = m_pProviders->GetHead();
- while(pElem != NULL)
- {
- EventPipeSessionProvider *pCandidate = pElem->GetValue();
- if(wcscmp(providerName, pCandidate->GetProviderName()) == 0)
- {
- pSessionProvider = pCandidate;
- break;
- }
- pElem = m_pProviders->GetNext(pElem);
- }
-
- return pSessionProvider;
-}
-
-bool EventPipeSessionProviderList::IsEmpty() const
-{
- LIMITED_METHOD_CONTRACT;
-
- return (m_pProviders->IsEmpty() && m_pCatchAllProvider == NULL);
-}
-
-EventPipeSessionProvider::EventPipeSessionProvider(
- LPCWSTR providerName,
- UINT64 keywords,
- EventPipeEventLevel loggingLevel,
- LPCWSTR filterData)
-{
- CONTRACTL
- {
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- if(providerName != NULL)
- {
- size_t bufSize = wcslen(providerName) + 1;
- m_pProviderName = new WCHAR[bufSize];
- wcscpy_s(m_pProviderName, bufSize, providerName);
- }
- else
- {
- m_pProviderName = NULL;
- }
- m_keywords = keywords;
- m_loggingLevel = loggingLevel;
-
- if(filterData != NULL)
- {
- size_t bufSize = wcslen(filterData) + 1;
- m_pFilterData = new WCHAR[bufSize];
- wcscpy_s(m_pFilterData, bufSize, filterData);
- }
- else
- {
- m_pFilterData = NULL;
- }
-}
-
-EventPipeSessionProvider::~EventPipeSessionProvider()
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- // C++ standard, $5.3.5/2: Deleting a NULL pointer is safe.
- delete[] m_pProviderName;
- m_pProviderName = NULL;
-
- delete[] m_pFilterData;
- m_pFilterData = NULL;
-}
-
-LPCWSTR EventPipeSessionProvider::GetProviderName() const
-{
- LIMITED_METHOD_CONTRACT;
- return m_pProviderName;
-}
-
-UINT64 EventPipeSessionProvider::GetKeywords() const
-{
- LIMITED_METHOD_CONTRACT;
- return m_keywords;
-}
-
-EventPipeEventLevel EventPipeSessionProvider::GetLevel() const
-{
- LIMITED_METHOD_CONTRACT;
- return m_loggingLevel;
-}
-
-LPCWSTR EventPipeSessionProvider::GetFilterData() const
-{
- LIMITED_METHOD_CONTRACT;
- return m_pFilterData;
-}
-
#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipesession.h b/src/vm/eventpipesession.h
index 3c4f293407..0474c7cafe 100644
--- a/src/vm/eventpipesession.h
+++ b/src/vm/eventpipesession.h
@@ -7,15 +7,14 @@
#ifdef FEATURE_PERFTRACING
-enum class EventPipeEventLevel;
-struct EventPipeProviderConfiguration;
class EventPipeSessionProviderList;
class EventPipeSessionProvider;
enum class EventPipeSessionType
{
File,
- Streaming
+ Streaming,
+ IpcStream
};
class EventPipeSession
@@ -49,10 +48,9 @@ public:
EventPipeSession(
EventPipeSessionType sessionType,
unsigned int circularBufferSizeInMB,
- EventPipeProviderConfiguration *pProviders,
- unsigned int numProviders,
- UINT64 multiFileTraceLengthInSeconds);
-
+ const EventPipeProviderConfiguration *pProviders,
+ uint32_t numProviders,
+ uint64_t multiFileTraceLengthInSeconds);
~EventPipeSession();
// Determine if the session is valid or not. Invalid sessions can be detected before they are enabled.
@@ -113,68 +111,6 @@ public:
EventPipeSessionProvider* GetSessionProvider(EventPipeProvider *pProvider);
};
-class EventPipeSessionProviderList
-{
-
-private:
-
- // The list of providers.
- SList<SListElem<EventPipeSessionProvider*>> *m_pProviders;
-
- // A catch-all provider used when tracing is enabled for all events.
- EventPipeSessionProvider *m_pCatchAllProvider;
-
-public:
-
- // Create a new list based on the input.
- EventPipeSessionProviderList(EventPipeProviderConfiguration *pConfigs, unsigned int numConfigs);
- ~EventPipeSessionProviderList();
-
- // Add a new session provider to the list.
- void AddSessionProvider(EventPipeSessionProvider *pProvider);
-
- // Get the session provider for the specified provider.
- // Return NULL if one doesn't exist.
- EventPipeSessionProvider* GetSessionProvider(EventPipeProvider *pProvider);
-
- // Returns true if the list is empty.
- bool IsEmpty() const;
-};
-
-class EventPipeSessionProvider
-{
-private:
-
- // The provider name.
- WCHAR *m_pProviderName;
-
- // The enabled keywords.
- UINT64 m_keywords;
-
- // The loging level.
- EventPipeEventLevel m_loggingLevel;
-
- // The filter data.
- WCHAR *m_pFilterData;
-
-public:
-
- EventPipeSessionProvider(
- LPCWSTR providerName,
- UINT64 keywords,
- EventPipeEventLevel loggingLevel,
- LPCWSTR filterData);
- ~EventPipeSessionProvider();
-
- LPCWSTR GetProviderName() const;
-
- UINT64 GetKeywords() const;
-
- EventPipeEventLevel GetLevel() const;
-
- LPCWSTR GetFilterData() const;
-};
-
#endif // FEATURE_PERFTRACING
#endif // __EVENTPIPE_SESSION_H__
diff --git a/src/vm/eventpipesessionprovider.cpp b/src/vm/eventpipesessionprovider.cpp
new file mode 100644
index 0000000000..7e79c47958
--- /dev/null
+++ b/src/vm/eventpipesessionprovider.cpp
@@ -0,0 +1,195 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include "common.h"
+#include "eventpipeprovider.h"
+#include "eventpipesessionprovider.h"
+
+#ifdef FEATURE_PERFTRACING
+
+EventPipeSessionProvider::EventPipeSessionProvider(
+ LPCWSTR providerName,
+ UINT64 keywords,
+ EventPipeEventLevel loggingLevel,
+ LPCWSTR filterData)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (providerName != NULL)
+ {
+ size_t bufSize = wcslen(providerName) + 1;
+ m_pProviderName = new WCHAR[bufSize];
+ wcscpy_s(m_pProviderName, bufSize, providerName);
+ }
+ else
+ {
+ m_pProviderName = NULL;
+ }
+
+ m_keywords = keywords;
+ m_loggingLevel = loggingLevel;
+
+ if (filterData != NULL)
+ {
+ size_t bufSize = wcslen(filterData) + 1;
+ m_pFilterData = new WCHAR[bufSize];
+ wcscpy_s(m_pFilterData, bufSize, filterData);
+ }
+ else
+ {
+ m_pFilterData = NULL;
+ }
+}
+
+EventPipeSessionProvider::~EventPipeSessionProvider()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ delete[] m_pProviderName;
+ delete[] m_pFilterData;
+}
+
+EventPipeSessionProviderList::EventPipeSessionProviderList(
+ const EventPipeProviderConfiguration *pConfigs,
+ uint32_t numConfigs)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION((numConfigs == 0) || (numConfigs > 0 && pConfigs != nullptr));
+ }
+ CONTRACTL_END;
+
+ m_pProviders = new SList<SListElem<EventPipeSessionProvider *>>();
+ m_pCatchAllProvider = NULL;
+
+ if ((numConfigs > 0) && (pConfigs == nullptr))
+ return; // TODO: This seems the logical thing to do here.
+
+ for (uint32_t i = 0; i < numConfigs; ++i)
+ {
+ const EventPipeProviderConfiguration *pConfig = &pConfigs[i];
+
+ // Enable all events if the provider name == '*', all keywords are on and the requested level == verbose.
+ if ((wcscmp(W("*"), pConfig->GetProviderName()) == 0) && (pConfig->GetKeywords() == 0xFFFFFFFFFFFFFFFF) && ((EventPipeEventLevel)pConfig->GetLevel() == EventPipeEventLevel::Verbose) && (m_pCatchAllProvider == NULL))
+ {
+ m_pCatchAllProvider = new EventPipeSessionProvider(NULL, 0xFFFFFFFFFFFFFFFF, EventPipeEventLevel::Verbose, NULL);
+ }
+ else
+ {
+ EventPipeSessionProvider *pProvider = new EventPipeSessionProvider(
+ pConfig->GetProviderName(),
+ pConfig->GetKeywords(),
+ (EventPipeEventLevel)pConfig->GetLevel(),
+ pConfig->GetFilterData());
+
+ m_pProviders->InsertTail(new SListElem<EventPipeSessionProvider *>(pProvider));
+ }
+ }
+}
+
+EventPipeSessionProviderList::~EventPipeSessionProviderList()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ if (m_pProviders != NULL)
+ {
+ SListElem<EventPipeSessionProvider *> *pElem = m_pProviders->GetHead();
+ while (pElem != NULL)
+ {
+ EventPipeSessionProvider *pProvider = pElem->GetValue();
+ delete pProvider;
+
+ SListElem<EventPipeSessionProvider *> *pCurElem = pElem;
+ pElem = m_pProviders->GetNext(pElem);
+ delete pCurElem;
+ }
+
+ delete m_pProviders;
+ }
+
+ delete m_pCatchAllProvider;
+}
+
+void EventPipeSessionProviderList::AddSessionProvider(EventPipeSessionProvider *pProvider)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(pProvider != nullptr);
+ }
+ CONTRACTL_END;
+
+ if (pProvider != nullptr)
+ m_pProviders->InsertTail(new SListElem<EventPipeSessionProvider *>(pProvider));
+}
+
+EventPipeSessionProvider *EventPipeSessionProviderList::GetSessionProvider(
+ EventPipeProvider *pProvider)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(pProvider != nullptr); // TODO: This seems like a reasonable pre-condition
+ }
+ CONTRACTL_END;
+
+ // Exists when tracing was enabled at start-up and all events were requested. This is a diagnostic config.
+ if (m_pCatchAllProvider != NULL)
+ return m_pCatchAllProvider;
+
+ if (m_pProviders == NULL)
+ return NULL;
+
+ SString providerNameStr = pProvider->GetProviderName();
+ LPCWSTR providerName = providerNameStr.GetUnicode();
+
+ EventPipeSessionProvider *pSessionProvider = NULL;
+ SListElem<EventPipeSessionProvider *> *pElem = m_pProviders->GetHead();
+ while (pElem != NULL)
+ {
+ EventPipeSessionProvider *pCandidate = pElem->GetValue();
+ if (wcscmp(providerName, pCandidate->GetProviderName()) == 0)
+ {
+ pSessionProvider = pCandidate;
+ break;
+ }
+ pElem = m_pProviders->GetNext(pElem);
+ }
+
+ return pSessionProvider;
+}
+
+bool EventPipeSessionProviderList::IsEmpty() const
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return (m_pProviders->IsEmpty() && m_pCatchAllProvider == NULL);
+}
+
+#endif // FEATURE_PERFTRACING
diff --git a/src/vm/eventpipesessionprovider.h b/src/vm/eventpipesessionprovider.h
new file mode 100644
index 0000000000..7b500e4412
--- /dev/null
+++ b/src/vm/eventpipesessionprovider.h
@@ -0,0 +1,81 @@
+#ifndef __EVENTPIPE_SESSION_PROVIDER_SESSION_H__
+#define __EVENTPIPE_SESSION_PROVIDER_SESSION_H__
+
+#ifdef FEATURE_PERFTRACING
+
+enum class EventPipeEventLevel;
+class EventPipeProvider;
+
+class EventPipeSessionProvider
+{
+public:
+ EventPipeSessionProvider(
+ LPCWSTR providerName,
+ UINT64 keywords,
+ EventPipeEventLevel loggingLevel,
+ LPCWSTR filterData);
+ ~EventPipeSessionProvider();
+
+ LPCWSTR GetProviderName() const
+ {
+ return m_pProviderName;
+ }
+
+ UINT64 GetKeywords() const
+ {
+ return m_keywords;
+ }
+
+ EventPipeEventLevel GetLevel() const
+ {
+ return m_loggingLevel;
+ }
+
+ LPCWSTR GetFilterData() const
+ {
+ return m_pFilterData;
+ }
+
+private:
+ WCHAR *m_pProviderName;
+ UINT64 m_keywords;
+ EventPipeEventLevel m_loggingLevel;
+ WCHAR *m_pFilterData;
+};
+
+class EventPipeSessionProviderList
+{
+public:
+
+ // Create a new list based on the input.
+ EventPipeSessionProviderList(
+ const EventPipeProviderConfiguration *pConfigs,
+ uint32_t numConfigs);
+ ~EventPipeSessionProviderList();
+
+ // Add a new session provider to the list.
+ void AddSessionProvider(EventPipeSessionProvider *pProvider);
+
+ // Get the session provider for the specified provider.
+ // Return NULL if one doesn't exist.
+ EventPipeSessionProvider* GetSessionProvider(EventPipeProvider *pProvider);
+
+ // Returns true if the list is empty.
+ bool IsEmpty() const;
+
+ EventPipeSessionProviderList() = delete;
+ EventPipeSessionProviderList(const EventPipeSessionProviderList &other) = delete;
+ EventPipeSessionProviderList(EventPipeSessionProviderList &&other) = delete;
+ EventPipeSessionProviderList &operator=(const EventPipeSessionProviderList &rhs) = delete;
+ EventPipeSessionProviderList &&operator=(EventPipeSessionProviderList &&rhs) = delete;
+
+private:
+ SList<SListElem<EventPipeSessionProvider*>> *m_pProviders = nullptr;
+
+ // A catch-all provider used when tracing is enabled for all events.
+ EventPipeSessionProvider *m_pCatchAllProvider = nullptr;
+};
+
+#endif // FEATURE_PERFTRACING
+
+#endif // __EVENTPIPE_SESSION_PROVIDER_SESSION_H__
diff --git a/src/vm/fastserializer.cpp b/src/vm/fastserializer.cpp
index 7a79b2ff19..e02ec93010 100644
--- a/src/vm/fastserializer.cpp
+++ b/src/vm/fastserializer.cpp
@@ -4,6 +4,7 @@
#include "common.h"
#include "fastserializer.h"
+#include "diagnosticsipc.h"
#ifdef FEATURE_PERFTRACING
@@ -11,7 +12,47 @@
// As a result of work on V3 of Event Pipe (https://github.com/Microsoft/perfview/pull/532) it got removed
// if you need it, please use git to restore it
-FastSerializer::FastSerializer(SString &outputFilePath)
+IpcStreamWriter::IpcStreamWriter(IpcStream *pStream) : _pStream(pStream)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(_pStream != nullptr);
+ }
+ CONTRACTL_END;
+}
+
+IpcStreamWriter::~IpcStreamWriter()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ delete _pStream;
+}
+
+bool IpcStreamWriter::Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_PREEMPTIVE;
+ PRECONDITION(lpBuffer != nullptr);
+ PRECONDITION(nBytesToWrite > 0);
+ }
+ CONTRACTL_END;
+
+ return _pStream->Write(lpBuffer, nBytesToWrite, nBytesWritten);
+}
+
+FileStreamWriter::FileStreamWriter(const SString &outputFilePath)
{
CONTRACTL
{
@@ -21,21 +62,17 @@ FastSerializer::FastSerializer(SString &outputFilePath)
}
CONTRACTL_END;
- m_writeErrorEncountered = false;
- m_currentPos = 0;
m_pFileStream = new CFileStream();
- if(FAILED(m_pFileStream->OpenForWrite(outputFilePath)))
+ if (FAILED(m_pFileStream->OpenForWrite(outputFilePath)))
{
_ASSERTE(!"Unable to open file for write.");
- delete(m_pFileStream);
+ delete m_pFileStream;
m_pFileStream = NULL;
return;
}
-
- WriteFileHeader();
}
-FastSerializer::~FastSerializer()
+FileStreamWriter::~FileStreamWriter()
{
CONTRACTL
{
@@ -45,18 +82,54 @@ FastSerializer::~FastSerializer()
}
CONTRACTL_END;
- if(m_pFileStream != NULL)
+ delete m_pFileStream;
+}
+
+bool FileStreamWriter::Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const
+{
+ CONTRACTL
{
- delete(m_pFileStream);
- m_pFileStream = NULL;
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_PREEMPTIVE;
+ PRECONDITION(lpBuffer != nullptr);
+ PRECONDITION(nBytesToWrite > 0);
+ }
+ CONTRACTL_END;
+
+ ULONG outCount;
+ HRESULT hResult = m_pFileStream->Write(lpBuffer, nBytesToWrite, &outCount);
+ nBytesWritten = static_cast<uint32_t>(outCount);
+ return hResult == S_OK;
+}
+
+FastSerializer::FastSerializer(StreamWriter *pStreamWriter) : m_pStreamWriter(pStreamWriter)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(m_pStreamWriter != NULL);
}
+ CONTRACTL_END;
+
+ m_writeErrorEncountered = false;
+ m_currentPos = 0;
+ WriteFileHeader();
}
-StreamLabel FastSerializer::GetStreamLabel() const
+FastSerializer::~FastSerializer()
{
- LIMITED_METHOD_CONTRACT;
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
- return (StreamLabel)m_currentPos;
+ delete m_pStreamWriter;
}
void FastSerializer::WriteObject(FastSerializableObject *pObject)
@@ -92,15 +165,13 @@ void FastSerializer::WriteBuffer(BYTE *pBuffer, unsigned int length)
}
CONTRACTL_END;
- if(m_writeErrorEncountered || m_pFileStream == NULL)
- {
+ if (m_writeErrorEncountered || m_pStreamWriter == NULL)
return;
- }
EX_TRY
{
- ULONG outCount;
- m_pFileStream->Write(pBuffer, length, &outCount);
+ uint32_t outCount;
+ m_pStreamWriter->Write(pBuffer, length, outCount);
#ifdef _DEBUG
size_t prevPos = m_currentPos;
@@ -120,7 +191,7 @@ void FastSerializer::WriteBuffer(BYTE *pBuffer, unsigned int length)
EX_CATCH
{
m_writeErrorEncountered = true;
- }
+ }
EX_END_CATCH(SwallowAllExceptions);
}
@@ -142,10 +213,10 @@ void FastSerializer::WriteSerializationType(FastSerializableObject *pObject)
WriteTag(FastSerializerTags::NullReference);
// Write the SerializationType version fields.
- int serializationType[2];
- serializationType[0] = pObject->GetObjectVersion();
- serializationType[1] = pObject->GetMinReaderVersion();
- WriteBuffer((BYTE*) &serializationType, sizeof(serializationType));
+ int serializationType[2] = {
+ pObject->GetObjectVersion(),
+ pObject->GetMinReaderVersion()};
+ WriteBuffer((BYTE *)&serializationType, sizeof(serializationType));
// Write the SerializationType TypeName field.
const char *strTypeName = pObject->GetTypeName();
@@ -157,7 +228,6 @@ void FastSerializer::WriteSerializationType(FastSerializableObject *pObject)
WriteTag(FastSerializerTags::EndObject);
}
-
void FastSerializer::WriteTag(FastSerializerTags tag, BYTE *payload, unsigned int payloadLength)
{
CONTRACTL
@@ -169,7 +239,7 @@ void FastSerializer::WriteTag(FastSerializerTags tag, BYTE *payload, unsigned in
CONTRACTL_END;
WriteBuffer((BYTE *)&tag, sizeof(tag));
- if(payload != NULL)
+ if (payload != NULL)
{
_ASSERTE(payloadLength > 0);
WriteBuffer(payload, payloadLength);
@@ -202,10 +272,10 @@ void FastSerializer::WriteString(const char *strContents, unsigned int length)
CONTRACTL_END;
// Write the string length .
- WriteBuffer((BYTE*) &length, sizeof(length));
+ WriteBuffer((BYTE *)&length, sizeof(length));
// Write the string contents.
- WriteBuffer((BYTE*) strContents, length);
+ WriteBuffer((BYTE *)strContents, length);
}
#endif // FEATURE_PERFTRACING
diff --git a/src/vm/fastserializer.h b/src/vm/fastserializer.h
index fad04de48c..98208c0eb1 100644
--- a/src/vm/fastserializer.h
+++ b/src/vm/fastserializer.h
@@ -12,22 +12,20 @@
#include "fastserializableobject.h"
#include "fstream.h"
-class FastSerializer;
-
-typedef unsigned int StreamLabel;
+class IpcStream;
// the enumeration has a specific set of values to keep it compatible with consumer library
// it's sibling is defined in https://github.com/Microsoft/perfview/blob/10d1f92b242c98073b3817ac5ee6d98cd595d39b/src/FastSerialization/FastSerialization.cs#L2295
-enum class FastSerializerTags : BYTE
+enum class FastSerializerTags : BYTE
{
- Error = 0, // To improve debugabilty, 0 is an illegal tag.
- NullReference = 1, // Tag for a null object forwardReference.
- ObjectReference = 2, // Followed by StreamLabel
- // 3 used to belong to ForwardReference, which got removed in V3
+ Error = 0, // To improve debugabilty, 0 is an illegal tag.
+ NullReference = 1, // Tag for a null object forwardReference.
+ ObjectReference = 2, // Followed by StreamLabel
+ // 3 used to belong to ForwardReference, which got removed in V3
BeginObject = 4, // Followed by Type object, object data, tagged EndObject
- BeginPrivateObject = 5, // Like beginObject, but not placed in interning table on deserialiation
- EndObject = 6, // Placed after an object to mark its end.
- // 7 used to belong to ForwardDefinition, which got removed in V3
+ BeginPrivateObject = 5, // Like beginObject, but not placed in interning table on deserialiation
+ EndObject = 6, // Placed after an object to mark its end.
+ // 7 used to belong to ForwardDefinition, which got removed in V3
Byte = 8,
Int16,
Int32,
@@ -35,17 +33,53 @@ enum class FastSerializerTags : BYTE
SkipRegion,
String,
Blob,
- Limit // Just past the last valid tag, used for asserts.
+ Limit // Just past the last valid tag, used for asserts.
};
-class FastSerializer
+//!
+//! Provides a generic interface for writing a sequence of bytes to a stream.
+//!
+class StreamWriter
{
public:
+ StreamWriter() = default;
+ virtual ~StreamWriter() = default;
+ virtual bool Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const = 0;
+};
- FastSerializer(SString &outputFilePath);
- ~FastSerializer();
+//!
+//! Implements a StreamWriter for writing bytes to an IPC.
+//!
+class IpcStreamWriter final : public StreamWriter
+{
+public:
+ IpcStreamWriter(IpcStream *pStream);
+ ~IpcStreamWriter();
+ bool Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const;
- StreamLabel GetStreamLabel() const;
+private:
+ IpcStream *const _pStream;
+};
+
+//!
+//! Implements a StreamWriter for writing bytes to an File.
+//!
+class FileStreamWriter final : public StreamWriter
+{
+public:
+ FileStreamWriter(const SString &outputFilePath);
+ ~FileStreamWriter();
+ bool Write(const void *lpBuffer, const uint32_t nBytesToWrite, uint32_t &nBytesWritten) const;
+
+private:
+ CFileStream *m_pFileStream;
+};
+
+class FastSerializer
+{
+public:
+ FastSerializer(StreamWriter *pStreamWriter);
+ ~FastSerializer();
void WriteObject(FastSerializableObject *pObject);
void WriteBuffer(BYTE *pBuffer, unsigned int length);
@@ -55,16 +89,14 @@ public:
size_t GetCurrentPosition() const
{
LIMITED_METHOD_CONTRACT;
-
return m_currentPos;
}
private:
-
void WriteSerializationType(FastSerializableObject *pObject);
void WriteFileHeader();
- CFileStream *m_pFileStream;
+ StreamWriter *const m_pStreamWriter;
bool m_writeErrorEncountered;
size_t m_currentPos;
};
diff --git a/src/vm/gcenv.os.cpp b/src/vm/gcenv.os.cpp
index 0e37931fe9..4d6a19c98d 100644
--- a/src/vm/gcenv.os.cpp
+++ b/src/vm/gcenv.os.cpp
@@ -494,16 +494,21 @@ static size_t GetRestrictedPhysicalMemoryLimit()
if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET) != 0)
job_workingset_limit = limit_info.BasicLimitInformation.MaximumWorkingSetSize;
- job_physical_memory_limit = min (job_memory_limit, job_process_memory_limit);
- job_physical_memory_limit = min (job_physical_memory_limit, job_workingset_limit);
+ if ((job_memory_limit != (size_t)MAX_PTR) ||
+ (job_process_memory_limit != (size_t)MAX_PTR) ||
+ (job_workingset_limit != (size_t)MAX_PTR))
+ {
+ job_physical_memory_limit = min (job_memory_limit, job_process_memory_limit);
+ job_physical_memory_limit = min (job_physical_memory_limit, job_workingset_limit);
- MEMORYSTATUSEX ms;
- ::GetProcessMemoryLoad(&ms);
- total_virtual = ms.ullTotalVirtual;
- total_physical = ms.ullAvailPhys;
+ MEMORYSTATUSEX ms;
+ ::GetProcessMemoryLoad(&ms);
+ total_virtual = ms.ullTotalVirtual;
+ total_physical = ms.ullAvailPhys;
- // A sanity check in case someone set a larger limit than there is actual physical memory.
- job_physical_memory_limit = (size_t) min (job_physical_memory_limit, ms.ullTotalPhys);
+ // A sanity check in case someone set a larger limit than there is actual physical memory.
+ job_physical_memory_limit = (size_t) min (job_physical_memory_limit, ms.ullTotalPhys);
+ }
}
}
diff --git a/src/vm/gchandleutilities.cpp b/src/vm/gchandleutilities.cpp
index 220445a1cc..a4fdec3c3f 100644
--- a/src/vm/gchandleutilities.cpp
+++ b/src/vm/gchandleutilities.cpp
@@ -33,7 +33,7 @@ void ValidateHandleAssignment(OBJECTHANDLE handle, OBJECTREF objRef)
#endif
IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager();
- ADIndex appDomainIndex = ADIndex(reinterpret_cast<DWORD>(mgr->GetHandleContext(handle)));
+ ADIndex appDomainIndex = ADIndex((DWORD)((SIZE_T)mgr->GetHandleContext(handle)));
ValidateObjectAndAppDomain(objRef, appDomainIndex);
#endif // _DEBUG_IMPL
diff --git a/src/vm/gchandleutilities.h b/src/vm/gchandleutilities.h
index 86227cdd6a..8c26737284 100644
--- a/src/vm/gchandleutilities.h
+++ b/src/vm/gchandleutilities.h
@@ -42,7 +42,7 @@ inline OBJECTREF ObjectFromHandle(OBJECTHANDLE handle)
#if defined(_DEBUG_IMPL) && !defined(DACCESS_COMPILE)
// not allowed to dispatch virtually on a IGCHandleManager when compiling for DAC
- DWORD context = (DWORD)GCHandleUtilities::GetGCHandleManager()->GetHandleContext(handle);
+ DWORD context = (DWORD)(SIZE_T)GCHandleUtilities::GetGCHandleManager()->GetHandleContext(handle);
OBJECTREF objRef = ObjectToOBJECTREF(*(Object**)handle);
ValidateObjectAndAppDomain(objRef, ADIndex(context));
diff --git a/src/vm/gchelpers.cpp b/src/vm/gchelpers.cpp
index a52e10bb4f..af3a1602b3 100644
--- a/src/vm/gchelpers.cpp
+++ b/src/vm/gchelpers.cpp
@@ -981,6 +981,8 @@ STRINGREF SlowAllocateString( DWORD cchStringLength )
// Limit the maximum string size to <2GB to mitigate risk of security issues caused by 32-bit integer
// overflows in buffer size calculations.
+ //
+ // If the value below is changed, also change SlowAllocateUtf8String.
if (cchStringLength > 0x3FFFFFDF)
ThrowOutOfMemory();
@@ -1028,6 +1030,81 @@ STRINGREF SlowAllocateString( DWORD cchStringLength )
return( ObjectToSTRINGREF(orObject) );
}
+#ifdef FEATURE_UTF8STRING
+UTF8STRINGREF SlowAllocateUtf8String(DWORD cchStringLength)
+{
+ CONTRACTL{
+ THROWS;
+ GC_TRIGGERS;
+ MODE_COOPERATIVE; // returns an objref without pinning it => cooperative
+ } CONTRACTL_END;
+
+ Utf8StringObject *orObject = NULL;
+
+#ifdef _DEBUG
+ if (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP))
+ {
+ char *a = new char;
+ delete a;
+ }
+#endif
+
+ // Limit the maximum string size to <2GB to mitigate risk of security issues caused by 32-bit integer
+ // overflows in buffer size calculations.
+ //
+ // 0x7FFFFFBF is derived from the const 0x3FFFFFDF in SlowAllocateString.
+ // Adding +1 (for null terminator) and multiplying by sizeof(WCHAR) means that
+ // SlowAllocateString allows a maximum of 0x7FFFFFC0 bytes to be used for the
+ // string data itself, with some additional buffer for object headers and other
+ // data. Since we don't have the sizeof(WCHAR) multiplication here, we only need
+ // -1 to account for the null terminator, leading to a max size of 0x7FFFFFBF.
+ if (cchStringLength > 0x7FFFFFBF)
+ ThrowOutOfMemory();
+
+ SIZE_T ObjectSize = PtrAlign(Utf8StringObject::GetSize(cchStringLength));
+ _ASSERTE(ObjectSize > cchStringLength);
+
+ SetTypeHandleOnThreadForAlloc(TypeHandle(g_pUtf8StringClass));
+
+ orObject = (Utf8StringObject *)Alloc(ObjectSize, FALSE, FALSE);
+
+ // Object is zero-init already
+ _ASSERTE(orObject->HasEmptySyncBlockInfo());
+
+ // Initialize Object
+ orObject->SetMethodTable(g_pUtf8StringClass);
+ orObject->SetLength(cchStringLength);
+
+ if (ObjectSize >= LARGE_OBJECT_SIZE)
+ {
+ GCHeapUtilities::GetGCHeap()->PublishObject((BYTE*)orObject);
+ }
+
+ // Notify the profiler of the allocation
+ if (TrackAllocations())
+ {
+ OBJECTREF objref = ObjectToOBJECTREF((Object*)orObject);
+ GCPROTECT_BEGIN(objref);
+ ProfilerObjectAllocatedCallback(objref, (ClassID)orObject->GetTypeHandle().AsPtr());
+ GCPROTECT_END();
+
+ orObject = (Utf8StringObject *)OBJECTREFToObject(objref);
+ }
+
+#ifdef FEATURE_EVENT_TRACE
+ // Send ETW event for allocation
+ if (ETW::TypeSystemLog::IsHeapAllocEventEnabled())
+ {
+ ETW::TypeSystemLog::SendObjectAllocatedEvent(orObject);
+ }
+#endif // FEATURE_EVENT_TRACE
+
+ LogAlloc(ObjectSize, g_pUtf8StringClass, orObject);
+
+ return( ObjectToUTF8STRINGREF(orObject) );
+}
+#endif // FEATURE_UTF8STRING
+
#ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
// OBJECTREF AllocateComClassObject(ComClassFactory* pComClsFac)
void AllocateComClassObject(ComClassFactory* pComClsFac, OBJECTREF* ppRefClass)
diff --git a/src/vm/gchelpers.h b/src/vm/gchelpers.h
index 0e407c6e61..8f6a16ade9 100644
--- a/src/vm/gchelpers.h
+++ b/src/vm/gchelpers.h
@@ -71,6 +71,10 @@ STRINGREF AllocateString( DWORD cchStringLength );
// The slow version, implemented in gcscan.cpp
STRINGREF SlowAllocateString( DWORD cchStringLength );
+#ifdef FEATURE_UTF8STRING
+UTF8STRINGREF SlowAllocateUtf8String( DWORD cchStringLength );
+#endif // FEATURE_UTF8STRING
+
#else
// On other platforms, go to the (somewhat less efficient) implementations in gcscan.cpp
@@ -83,6 +87,10 @@ OBJECTREF AllocateObjectArray(DWORD cElements, TypeHandle ElementType, BOOL bAll
STRINGREF SlowAllocateString( DWORD cchStringLength );
+#ifdef FEATURE_UTF8STRING
+UTF8STRINGREF SlowAllocateUtf8String( DWORD cchStringLength );
+#endif // FEATURE_UTF8STRING
+
inline STRINGREF AllocateString( DWORD cchStringLength )
{
WRAPPER_NO_CONTRACT;
@@ -92,6 +100,15 @@ inline STRINGREF AllocateString( DWORD cchStringLength )
#endif
+#ifdef FEATURE_UTF8STRING
+inline UTF8STRINGREF AllocateUtf8String(DWORD cchStringLength)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return SlowAllocateUtf8String(cchStringLength);
+}
+#endif // FEATURE_UTF8STRING
+
OBJECTREF DupArrayForCloning(BASEARRAYREF pRef, BOOL bAllocateInLargeHeap = FALSE);
// The JIT requests the EE to specify an allocation helper to use at each new-site.
diff --git a/src/vm/gdbjit.cpp b/src/vm/gdbjit.cpp
index 4ab5336f49..d7803f0b3e 100644
--- a/src/vm/gdbjit.cpp
+++ b/src/vm/gdbjit.cpp
@@ -15,7 +15,11 @@
#include "gdbjit.h"
#include "gdbjithelpers.h"
+#ifndef __GNUC__
__declspec(thread) bool tls_isSymReaderInProgress = false;
+#else // !__GNUC__
+thread_local bool tls_isSymReaderInProgress = false;
+#endif // !__GNUC__
#ifdef _DEBUG
static void DumpElf(const char* methodName, const char *addr, size_t size)
diff --git a/src/vm/ilmarshalers.cpp b/src/vm/ilmarshalers.cpp
index 2b7f7a6cdf..7d8f04671c 100644
--- a/src/vm/ilmarshalers.cpp
+++ b/src/vm/ilmarshalers.cpp
@@ -3292,6 +3292,92 @@ ILCriticalHandleMarshaler::ReturnOverride(
return OVERRIDDEN;
} // ILCriticalHandleMarshaler::ReturnOverride
+MarshalerOverrideStatus ILBlittableValueClassWithCopyCtorMarshaler::ArgumentOverride(NDirectStubLinker* psl,
+ BOOL byref,
+ BOOL fin,
+ BOOL fout,
+ BOOL fManagedToNative,
+ OverrideProcArgs* pargs,
+ UINT* pResID,
+ UINT argidx,
+ UINT nativeStackOffset)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ ILCodeStream* pslIL = psl->GetMarshalCodeStream();
+ ILCodeStream* pslILDispatch = psl->GetDispatchCodeStream();
+
+ if (byref)
+ {
+ *pResID = IDS_EE_BADMARSHAL_COPYCTORRESTRICTION;
+ return DISALLOWED;
+ }
+
+ if (fManagedToNative)
+ {
+ // 1) create new native value type local
+ // 2) run new->CopyCtor(old)
+ // 3) run old->Dtor()
+
+ LocalDesc locDesc(pargs->mm.m_pMT);
+
+ DWORD dwNewValueTypeLocal;
+
+ // Step 1
+ dwNewValueTypeLocal = pslIL->NewLocal(locDesc);
+
+ // Step 2
+ if (pargs->mm.m_pCopyCtor)
+ {
+ // Managed copy constructor has signature of CopyCtor(T* new, T old);
+ pslIL->EmitLDLOCA(dwNewValueTypeLocal);
+ pslIL->EmitLDARG(argidx);
+ pslIL->EmitCALL(pslIL->GetToken(pargs->mm.m_pCopyCtor), 2, 0);
+ }
+ else
+ {
+ pslIL->EmitLDARG(argidx);
+ pslIL->EmitLDOBJ(pslIL->GetToken(pargs->mm.m_pMT));
+ pslIL->EmitSTLOC(dwNewValueTypeLocal);
+ }
+
+ // Step 3
+ if (pargs->mm.m_pDtor)
+ {
+ // Managed destructor has signature of Destructor(T old);
+ pslIL->EmitLDARG(argidx);
+ pslIL->EmitCALL(pslIL->GetToken(pargs->mm.m_pDtor), 1, 0);
+ }
+#ifdef _TARGET_X86_
+ pslIL->SetStubTargetArgType(&locDesc); // native type is the value type
+ pslILDispatch->EmitLDLOC(dwNewValueTypeLocal); // we load the local directly
+#else
+ pslIL->SetStubTargetArgType(ELEMENT_TYPE_I); // native type is a pointer
+ pslILDispatch->EmitLDLOCA(dwNewValueTypeLocal);
+#endif
+
+ return OVERRIDDEN;
+ }
+ else
+ {
+ // nothing to do but pass the value along
+ // note that on x86 the argument comes by-value but is converted to pointer by the UM thunk
+ // so that we don't make copies that would not be accounted for by copy ctors
+ LocalDesc locDesc(pargs->mm.m_pMT);
+ locDesc.MakeCopyConstructedPointer();
+
+ pslIL->SetStubTargetArgType(&locDesc); // native type is a pointer
+ pslILDispatch->EmitLDARG(argidx);
+
+ return OVERRIDDEN;
+ }
+}
LocalDesc ILArgIteratorMarshaler::GetNativeType()
{
diff --git a/src/vm/ilmarshalers.h b/src/vm/ilmarshalers.h
index 99a3d8f8d6..c892ae66a9 100644
--- a/src/vm/ilmarshalers.h
+++ b/src/vm/ilmarshalers.h
@@ -2725,8 +2725,40 @@ protected:
virtual void EmitConvertContentsNativeToCLR(ILCodeStream* pslILEmit);
};
+class ILBlittableValueClassWithCopyCtorMarshaler : public ILMarshaler
+{
+public:
+ enum
+ {
+ c_fInOnly = TRUE,
+ c_nativeSize = VARIABLESIZE,
+ c_CLRSize = sizeof(OBJECTREF),
+ };
+ LocalDesc GetManagedType()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return LocalDesc();
+ }
+ LocalDesc GetNativeType()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return LocalDesc();
+ }
+
+ static MarshalerOverrideStatus ArgumentOverride(NDirectStubLinker* psl,
+ BOOL byref,
+ BOOL fin,
+ BOOL fout,
+ BOOL fManagedToNative,
+ OverrideProcArgs* pargs,
+ UINT* pResID,
+ UINT argidx,
+ UINT nativeStackOffset);
+
+
+};
class ILArgIteratorMarshaler : public ILMarshaler
{
diff --git a/src/vm/jithelpers.cpp b/src/vm/jithelpers.cpp
index 303f06130f..0576ca7336 100644
--- a/src/vm/jithelpers.cpp
+++ b/src/vm/jithelpers.cpp
@@ -2895,6 +2895,61 @@ HCIMPL1(StringObject*, AllocateString_MP_FastPortable, DWORD stringLength)
}
HCIMPLEND
+#ifdef FEATURE_UTF8STRING
+HCIMPL1(Utf8StringObject*, AllocateUtf8String_MP_FastPortable, DWORD stringLength)
+{
+ FCALL_CONTRACT;
+
+ do
+ {
+ _ASSERTE(GCHeapUtilities::UseThreadAllocationContexts());
+
+ // Instead of doing elaborate overflow checks, we just limit the number of elements. This will avoid all overflow
+ // problems, as well as making sure big string objects are correctly allocated in the big object heap.
+ if (stringLength >= LARGE_OBJECT_SIZE - 256)
+ {
+ break;
+ }
+
+ // This is typically the only call in the fast path. Making the call early seems to be better, as it allows the compiler
+ // to use volatile registers for intermediate values. This reduces the number of push/pop instructions and eliminates
+ // some reshuffling of intermediate values into nonvolatile registers around the call.
+ Thread *thread = GetThread();
+
+ SIZE_T totalSize = Utf8StringObject::GetSize(stringLength);
+
+ // The method table's base size includes space for a terminating null character
+ _ASSERTE(totalSize >= g_pUtf8StringClass->GetBaseSize());
+ _ASSERTE(totalSize - g_pUtf8StringClass->GetBaseSize() == stringLength);
+
+ SIZE_T alignedTotalSize = ALIGN_UP(totalSize, DATA_ALIGNMENT);
+ _ASSERTE(alignedTotalSize >= totalSize);
+ totalSize = alignedTotalSize;
+
+ gc_alloc_context *allocContext = thread->GetAllocContext();
+ BYTE *allocPtr = allocContext->alloc_ptr;
+ _ASSERTE(allocPtr <= allocContext->alloc_limit);
+ if (totalSize > static_cast<SIZE_T>(allocContext->alloc_limit - allocPtr))
+ {
+ break;
+ }
+ allocContext->alloc_ptr = allocPtr + totalSize;
+
+ _ASSERTE(allocPtr != nullptr);
+ Utf8StringObject *stringObject = reinterpret_cast<Utf8StringObject *>(allocPtr);
+ stringObject->SetMethodTable(g_pUtf8StringClass);
+ stringObject->SetLength(stringLength);
+
+ return stringObject;
+ } while (false);
+
+ // Tail call to the slow helper
+ ENDFORBIDGC();
+ return HCCALL1(FramedAllocateUtf8String, stringLength);
+}
+HCIMPLEND
+#endif // FEATURE_UTF8STRING
+
#include <optdefault.h>
/*********************************************************************/
@@ -2933,6 +2988,22 @@ HCIMPL1(StringObject*, FramedAllocateString, DWORD stringLength)
}
HCIMPLEND
+#ifdef FEATURE_UTF8STRING
+HCIMPL1(Utf8StringObject*, FramedAllocateUtf8String, DWORD stringLength)
+{
+ FCALL_CONTRACT;
+
+ UTF8STRINGREF result = NULL;
+ HELPER_METHOD_FRAME_BEGIN_RET_0(); // Set up a frame
+
+ result = SlowAllocateUtf8String(stringLength);
+
+ HELPER_METHOD_FRAME_END();
+ return((Utf8StringObject*) OBJECTREFToObject(result));
+}
+HCIMPLEND
+#endif // FEATURE_UTF8STRING
+
/*********************************************************************/
OBJECTHANDLE ConstructStringLiteral(CORINFO_MODULE_HANDLE scopeHnd, mdToken metaTok)
{
diff --git a/src/vm/jitinterface.cpp b/src/vm/jitinterface.cpp
index 6e4b288d66..b3ede3baa2 100644
--- a/src/vm/jitinterface.cpp
+++ b/src/vm/jitinterface.cpp
@@ -7493,6 +7493,56 @@ bool getILIntrinsicImplementationForRuntimeHelpers(MethodDesc * ftn,
return true;
}
+ if (tk == MscorlibBinder::GetMethod(METHOD__RUNTIME_HELPERS__IS_BITWISE_EQUATABLE)->GetMemberDef())
+ {
+ _ASSERTE(ftn->HasMethodInstantiation());
+ Instantiation inst = ftn->GetMethodInstantiation();
+
+ _ASSERTE(ftn->GetNumGenericMethodArgs() == 1);
+ TypeHandle typeHandle = inst[0];
+ MethodTable * methodTable = typeHandle.GetMethodTable();
+
+ static const BYTE returnTrue[] = { CEE_LDC_I4_1, CEE_RET };
+ static const BYTE returnFalse[] = { CEE_LDC_I4_0, CEE_RET };
+
+ // Ideally we could detect automatically whether a type is trivially equatable
+ // (i.e., its operator == could be implemented via memcmp). But for now we'll
+ // do the simple thing and hardcode the list of types we know fulfill this contract.
+ // n.b. This doesn't imply that the type's CompareTo method can be memcmp-implemented,
+ // as a method like CompareTo may need to take a type's signedness into account.
+
+ if (methodTable == MscorlibBinder::GetClass(CLASS__BOOLEAN)
+ || methodTable == MscorlibBinder::GetClass(CLASS__BYTE)
+ || methodTable == MscorlibBinder::GetClass(CLASS__SBYTE)
+#ifdef FEATURE_UTF8STRING
+ || methodTable == MscorlibBinder::GetClass(CLASS__CHAR8)
+#endif // FEATURE_UTF8STRING
+ || methodTable == MscorlibBinder::GetClass(CLASS__CHAR)
+ || methodTable == MscorlibBinder::GetClass(CLASS__INT16)
+ || methodTable == MscorlibBinder::GetClass(CLASS__UINT16)
+ || methodTable == MscorlibBinder::GetClass(CLASS__INT32)
+ || methodTable == MscorlibBinder::GetClass(CLASS__UINT32)
+ || methodTable == MscorlibBinder::GetClass(CLASS__INT64)
+ || methodTable == MscorlibBinder::GetClass(CLASS__UINT64)
+ || methodTable == MscorlibBinder::GetClass(CLASS__INTPTR)
+ || methodTable == MscorlibBinder::GetClass(CLASS__UINTPTR)
+ || methodTable == MscorlibBinder::GetClass(CLASS__RUNE)
+ || methodTable->IsEnum())
+ {
+ methInfo->ILCode = const_cast<BYTE*>(returnTrue);
+ }
+ else
+ {
+ methInfo->ILCode = const_cast<BYTE*>(returnFalse);
+ }
+
+ methInfo->ILCodeSize = sizeof(returnTrue);
+ methInfo->maxStack = 1;
+ methInfo->EHcount = 0;
+ methInfo->options = (CorInfoOptions)0;
+ return true;
+ }
+
return false;
}
diff --git a/src/vm/jitinterface.h b/src/vm/jitinterface.h
index fe7dd4a922..af42bd29ab 100644
--- a/src/vm/jitinterface.h
+++ b/src/vm/jitinterface.h
@@ -231,6 +231,11 @@ extern FCDECL1(StringObject*, AllocateString_MP_FastPortable, DWORD stringLength
extern FCDECL1(StringObject*, UnframedAllocateString, DWORD stringLength);
extern FCDECL1(StringObject*, FramedAllocateString, DWORD stringLength);
+#ifdef FEATURE_UTF8STRING
+extern FCDECL1(Utf8StringObject*, AllocateUtf8String_MP_FastPortable, DWORD stringLength);
+extern FCDECL1(Utf8StringObject*, FramedAllocateUtf8String, DWORD stringLength);
+#endif // FEATURE_UTF8STRING
+
extern FCDECL2(Object*, JIT_NewArr1VC_MP_FastPortable, CORINFO_CLASS_HANDLE arrayMT, INT_PTR size);
extern FCDECL2(Object*, JIT_NewArr1OBJ_MP_FastPortable, CORINFO_CLASS_HANDLE arrayMT, INT_PTR size);
extern FCDECL2(Object*, JIT_NewArr1_R2R, CORINFO_CLASS_HANDLE arrayTypeHnd_, INT_PTR size);
diff --git a/src/vm/jitinterfacegen.cpp b/src/vm/jitinterfacegen.cpp
index f86011d3ef..3a5b618c26 100644
--- a/src/vm/jitinterfacegen.cpp
+++ b/src/vm/jitinterfacegen.cpp
@@ -80,6 +80,9 @@ void InitJITHelpers1()
SetJitHelperFunction(CORINFO_HELP_NEWARR_1_OBJ, JIT_NewArr1OBJ_MP_FastPortable);
ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateString_MP_FastPortable), ECall::FastAllocateString);
+#ifdef FEATURE_UTF8STRING
+ ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateUtf8String_MP_FastPortable), ECall::FastAllocateUtf8String);
+#endif // FEATURE_UTF8STRING
#else // FEATURE_PAL
// if (multi-proc || server GC)
if (GCHeapUtilities::UseThreadAllocationContexts())
@@ -91,6 +94,9 @@ void InitJITHelpers1()
SetJitHelperFunction(CORINFO_HELP_NEWARR_1_OBJ, JIT_NewArr1OBJ_MP_InlineGetThread);
ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateStringFastMP_InlineGetThread), ECall::FastAllocateString);
+#ifdef FEATURE_UTF8STRING
+ ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateUtf8String_MP_FastPortable), ECall::FastAllocateUtf8String);
+#endif // FEATURE_UTF8STRING
}
else
{
@@ -105,6 +111,9 @@ void InitJITHelpers1()
SetJitHelperFunction(CORINFO_HELP_NEWARR_1_OBJ, JIT_NewArr1OBJ_UP);
ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateStringFastUP), ECall::FastAllocateString);
+#ifdef FEATURE_UTF8STRING
+ ECall::DynamicallyAssignFCallImpl(GetEEFuncEntryPoint(AllocateUtf8String_MP_FastPortable), ECall::FastAllocateUtf8String);
+#endif // FEATURE_UTF8STRING
}
#endif // FEATURE_PAL
}
diff --git a/src/vm/marshalnative.cpp b/src/vm/marshalnative.cpp
index 334a4a88e4..bd4ad09731 100644
--- a/src/vm/marshalnative.cpp
+++ b/src/vm/marshalnative.cpp
@@ -266,6 +266,11 @@ FCIMPL1(FC_BOOL_RET, MarshalNative::IsPinnable, Object* obj)
if (obj->GetMethodTable() == g_pStringClass)
FC_RETURN_BOOL(TRUE);
+#ifdef FEATURE_UTF8STRING
+ if (obj->GetMethodTable() == g_pUtf8StringClass)
+ FC_RETURN_BOOL(TRUE);
+#endif // FEATURE_UTF8STRING
+
if (obj->GetMethodTable()->IsArray())
{
BASEARRAYREF asArray = (BASEARRAYREF)ObjectToOBJECTREF(obj);
@@ -527,6 +532,11 @@ void ValidatePinnedObject(OBJECTREF obj)
if (obj->GetMethodTable() == g_pStringClass)
return;
+#ifdef FEATURE_UTF8STRING
+ if (obj->GetMethodTable() == g_pUtf8StringClass)
+ return;
+#endif // FEATURE_UTF8STRING
+
if (obj->GetMethodTable()->IsArray())
{
BASEARRAYREF asArray = (BASEARRAYREF) obj;
@@ -1014,6 +1024,28 @@ FCIMPL1(Object*, MarshalNative::GetUniqueObjectForIUnknown, IUnknown* pUnk)
}
FCIMPLEND
+FCIMPL1(Object*, MarshalNative::GetUniqueObjectForIUnknownWithoutUnboxing, IUnknown* pUnk)
+{
+ FCALL_CONTRACT;
+
+ OBJECTREF oref = NULL;
+ HELPER_METHOD_FRAME_BEGIN_RET_1(oref);
+
+ HRESULT hr = S_OK;
+
+ if(!pUnk)
+ COMPlusThrowArgumentNull(W("pUnk"));
+
+ // Ensure COM is started up.
+ EnsureComStarted();
+
+ GetObjectRefFromComIP(&oref, pUnk, NULL, NULL, ObjFromComIP::UNIQUE_OBJECT | ObjFromComIP::IGNORE_WINRT_AND_SKIP_UNBOXING);
+
+ HELPER_METHOD_FRAME_END();
+ return OBJECTREFToObject(oref);
+}
+FCIMPLEND
+
//====================================================================
// return an Object for IUnknown, using the Type T,
// NOTE:
diff --git a/src/vm/marshalnative.h b/src/vm/marshalnative.h
index eaadfacde0..bac0c2475d 100644
--- a/src/vm/marshalnative.h
+++ b/src/vm/marshalnative.h
@@ -122,6 +122,11 @@ public:
static FCDECL1(Object*, GetUniqueObjectForIUnknown, IUnknown* pUnk);
//====================================================================
+ // return a unique cacheless Object for IUnknown
+ //====================================================================
+ static FCDECL1(Object*, GetUniqueObjectForIUnknownWithoutUnboxing, IUnknown* pUnk);
+
+ //====================================================================
// return an Object for IUnknown, using the Type T,
// NOTE:
// Type T should be either a COM imported Type or a sub-type of COM imported Type
diff --git a/src/vm/metasig.h b/src/vm/metasig.h
index 5321fd3ee3..5e0a821e44 100644
--- a/src/vm/metasig.h
+++ b/src/vm/metasig.h
@@ -402,6 +402,7 @@ DEFINE_METASIG(IM(Bool_Bool_RetStr, F F, s))
DEFINE_METASIG(IM(PtrChar_RetVoid, P(u), v))
DEFINE_METASIG(IM(PtrChar_Int_Int_RetVoid, P(u) i i, v))
+DEFINE_METASIG_T(IM(ReadOnlySpanOfByte_RetVoid, GI(g(READONLY_SPAN), 1, b), v))
DEFINE_METASIG_T(IM(ReadOnlySpanOfChar_RetVoid, GI(g(READONLY_SPAN), 1, u), v))
DEFINE_METASIG(IM(PtrSByt_RetVoid, P(B), v))
DEFINE_METASIG(IM(PtrSByt_Int_Int_RetVoid, P(B) i i, v))
@@ -420,6 +421,19 @@ DEFINE_METASIG(IM(PtrSByt_Int_Int_RetStr, P(B) i i, s))
DEFINE_METASIG_T(IM(PtrSByt_Int_Int_Encoding_RetStr, P(B) i i C(ENCODING), s))
DEFINE_METASIG(IM(Obj_Int_RetIntPtr, j i, I))
+DEFINE_METASIG(IM(ArrByte_Int_Int_RetVoid, a(b) i i, v))
+DEFINE_METASIG(IM(PtrByte_RetVoid, P(b), v))
+
+#ifdef FEATURE_UTF8STRING
+DEFINE_METASIG_T(IM(ReadOnlySpanOfByte_RetUtf8Str, GI(g(READONLY_SPAN), 1, b), C(UTF8_STRING)))
+DEFINE_METASIG_T(IM(ReadOnlySpanOfChar_RetUtf8Str, GI(g(READONLY_SPAN), 1, u), C(UTF8_STRING)))
+DEFINE_METASIG_T(IM(ArrByte_Int_Int_RetUtf8Str, a(b) i i, C(UTF8_STRING)))
+DEFINE_METASIG_T(IM(PtrByte_RetUtf8Str, P(b), C(UTF8_STRING)))
+DEFINE_METASIG_T(IM(ArrChar_Int_Int_RetUtf8Str, a(u) i i, C(UTF8_STRING)))
+DEFINE_METASIG_T(IM(PtrChar_RetUtf8Str, P(u), C(UTF8_STRING)))
+DEFINE_METASIG_T(IM(String_RetUtf8Str, s, C(UTF8_STRING)))
+#endif // FEATURE_UTF8STRING
+
DEFINE_METASIG(IM(Char_Char_RetStr, u u, s))
DEFINE_METASIG(IM(Char_Int_RetVoid, u i, v))
DEFINE_METASIG_T(SM(RetCultureInfo, _, C(CULTURE_INFO)))
diff --git a/src/vm/methodtable.h b/src/vm/methodtable.h
index 9f9b25e37b..84f8399dc2 100644
--- a/src/vm/methodtable.h
+++ b/src/vm/methodtable.h
@@ -1743,7 +1743,7 @@ public:
BOOL IsString()
{
LIMITED_METHOD_DAC_CONTRACT;
- return HasComponentSize() && !IsArray();
+ return HasComponentSize() && !IsArray() && RawGetComponentSize() == 2;
}
BOOL HasComponentSize() const
diff --git a/src/vm/methodtablebuilder.cpp b/src/vm/methodtablebuilder.cpp
index 568a23136e..d4ce5b0df0 100644
--- a/src/vm/methodtablebuilder.cpp
+++ b/src/vm/methodtablebuilder.cpp
@@ -9711,6 +9711,19 @@ void MethodTableBuilder::CheckForSystemTypes()
pMT->SetComponentSize(2);
}
+#ifdef FEATURE_UTF8STRING
+ else if (strcmp(name, g_Utf8StringName) == 0 && strcmp(nameSpace, g_SystemNS) == 0)
+ {
+ // Utf8Strings are not "normal" objects, so we need to mess with their method table a bit
+ // so that the GC can figure out how big each string is...
+ DWORD baseSize = Utf8StringObject::GetBaseSize();
+ pMT->SetBaseSize(baseSize); // NULL character included
+
+ GetHalfBakedClass()->SetBaseSizePadding(baseSize - bmtFP->NumInstanceFieldBytes);
+
+ pMT->SetComponentSize(1);
+ }
+#endif // FEATURE_UTF8STRING
else if (strcmp(name, g_CriticalFinalizerObjectName) == 0 && strcmp(nameSpace, g_ConstrainedExecutionNS) == 0)
{
// To introduce a class with a critical finalizer,
diff --git a/src/vm/mlinfo.cpp b/src/vm/mlinfo.cpp
index e007891aa8..f861c43cf8 100644
--- a/src/vm/mlinfo.cpp
+++ b/src/vm/mlinfo.cpp
@@ -1438,6 +1438,7 @@ MarshalInfo::MarshalInfo(Module* pModule,
CorNativeType nativeType = NATIVE_TYPE_DEFAULT;
Assembly *pAssembly = pModule->GetAssembly();
+ BOOL fNeedsCopyCtor = FALSE;
m_BestFit = BestFit;
m_ThrowOnUnmappableChar = ThrowOnUnmappableChar;
m_ms = ms;
@@ -1548,6 +1549,21 @@ MarshalInfo::MarshalInfo(Module* pModule,
IfFailGoto(sig.GetElemType(NULL), lFail);
mtype = sig.PeekElemTypeNormalized(pModule, pTypeContext);
+ // Check for Copy Constructor Modifier - peek closed elem type here to prevent ELEMENT_TYPE_VALUETYPE
+ // turning into a primitive.
+ if (sig.PeekElemTypeClosed(pModule, pTypeContext) == ELEMENT_TYPE_VALUETYPE)
+ {
+ // Skip ET_BYREF
+ IfFailGoto(sigtmp.GetByte(NULL), lFail);
+
+ if (sigtmp.HasCustomModifier(pModule, "Microsoft.VisualC.NeedsCopyConstructorModifier", ELEMENT_TYPE_CMOD_REQD) ||
+ sigtmp.HasCustomModifier(pModule, "System.Runtime.CompilerServices.IsCopyConstructed", ELEMENT_TYPE_CMOD_REQD) )
+ {
+ mtype = ELEMENT_TYPE_VALUETYPE;
+ fNeedsCopyCtor = TRUE;
+ m_byref = FALSE;
+ }
+ }
}
else
{
@@ -1590,6 +1606,19 @@ MarshalInfo::MarshalInfo(Module* pModule,
IfFailGoto(E_FAIL, lFail);
}
+ // Check for Copy Constructor Modifier
+ if (sigtmp.HasCustomModifier(pModule, "Microsoft.VisualC.NeedsCopyConstructorModifier", ELEMENT_TYPE_CMOD_REQD) ||
+ sigtmp.HasCustomModifier(pModule, "System.Runtime.CompilerServices.IsCopyConstructed", ELEMENT_TYPE_CMOD_REQD) )
+ {
+ mtype = mtype2;
+
+ // Keep the sig pointer in sync with mtype (skip ELEMENT_TYPE_PTR) because for the rest
+ // of this method we are pretending that the parameter is a value type passed by-value.
+ IfFailGoto(sig.GetElemType(NULL), lFail);
+
+ fNeedsCopyCtor = TRUE;
+ m_byref = FALSE;
+ }
}
}
else
@@ -1846,13 +1875,9 @@ MarshalInfo::MarshalInfo(Module* pModule,
break;
case ELEMENT_TYPE_I:
-#ifdef FEATURE_COMINTEROP
- if (IsWinRTScenario())
- {
- m_resID = IDS_EE_BADMARSHAL_WINRT_ILLEGAL_TYPE;
- IfFailGoto(E_FAIL, lFail);
- }
-#endif // FEATURE_COMINTEROP
+ // Technically the "native int" and "native uint" types aren't supported in the WinRT scenario,
+ // but we need to not block ourselves from using them to enable accurate managed->native marshalling of
+ // projected types such as NotifyCollectionChangedEventArgs and NotifyPropertyChangedEventArgs.
if (!(nativeType == NATIVE_TYPE_INT || nativeType == NATIVE_TYPE_UINT || nativeType == NATIVE_TYPE_DEFAULT))
{
@@ -1863,13 +1888,6 @@ MarshalInfo::MarshalInfo(Module* pModule,
break;
case ELEMENT_TYPE_U:
-#ifdef FEATURE_COMINTEROP
- if (IsWinRTScenario())
- {
- m_resID = IDS_EE_BADMARSHAL_WINRT_ILLEGAL_TYPE;
- IfFailGoto(E_FAIL, lFail);
- }
-#endif // FEATURE_COMINTEROP
if (!(nativeType == NATIVE_TYPE_UINT || nativeType == NATIVE_TYPE_INT || nativeType == NATIVE_TYPE_DEFAULT))
{
@@ -2684,6 +2702,29 @@ MarshalInfo::MarshalInfo(Module* pModule,
}
else
{
+ if (fNeedsCopyCtor)
+ {
+#ifdef FEATURE_COMINTEROP
+ if (m_ms == MARSHAL_SCENARIO_WINRT)
+ {
+ // our WinRT-optimized GetCOMIPFromRCW helpers don't support copy
+ // constructor stubs so make sure that this marshaler will not be used
+ m_resID = IDS_EE_BADMARSHAL_WINRT_COPYCTOR;
+ IfFailGoto(E_FAIL, lFail);
+ }
+#endif
+
+ MethodDesc *pCopyCtor;
+ MethodDesc *pDtor;
+ FindCopyCtor(pModule, m_pMT, &pCopyCtor);
+ FindDtor(pModule, m_pMT, &pDtor);
+
+ m_args.mm.m_pMT = m_pMT;
+ m_args.mm.m_pCopyCtor = pCopyCtor;
+ m_args.mm.m_pDtor = pDtor;
+ m_type = MARSHAL_TYPE_BLITTABLEVALUECLASSWITHCOPYCTOR;
+ }
+ else
#ifdef _TARGET_X86_
// JIT64 is not aware of normalized value types and this optimization
// (returning small value types by value in registers) is already done in JIT64.
@@ -3398,6 +3439,7 @@ UINT16 MarshalInfo::GetNativeSize(MarshalType mtype, MarshalScenario ms)
{
case MARSHAL_TYPE_BLITTABLEVALUECLASS:
case MARSHAL_TYPE_VALUECLASS:
+ case MARSHAL_TYPE_BLITTABLEVALUECLASSWITHCOPYCTOR:
return (UINT16) m_pMT->GetNativeSize();
default:
@@ -4049,6 +4091,7 @@ DispParamMarshaler *MarshalInfo::GenerateDispParamMarshaler()
case MARSHAL_TYPE_BLITTABLEVALUECLASS:
case MARSHAL_TYPE_BLITTABLEPTR:
case MARSHAL_TYPE_LAYOUTCLASSPTR:
+ case MARSHAL_TYPE_BLITTABLEVALUECLASSWITHCOPYCTOR:
pDispParamMarshaler = new DispParamRecordMarshaler(m_pMT);
break;
@@ -4350,6 +4393,9 @@ VOID MarshalInfo::MarshalTypeToString(SString& strMarshalType, BOOL fSizeIsSpeci
case MARSHAL_TYPE_ARGITERATOR:
strRetVal = W("ArgIterator");
break;
+ case MARSHAL_TYPE_BLITTABLEVALUECLASSWITHCOPYCTOR:
+ strRetVal = W("blittable value class with copy constructor");
+ break;
#ifdef FEATURE_COMINTEROP
case MARSHAL_TYPE_OBJECT:
strRetVal = W("VARIANT");
diff --git a/src/vm/mscorlib.cpp b/src/vm/mscorlib.cpp
index faec6e5c97..10ed4944b6 100644
--- a/src/vm/mscorlib.cpp
+++ b/src/vm/mscorlib.cpp
@@ -84,10 +84,12 @@
#if defined(FEATURE_EVENTSOURCE_XPLAT)
#include "nativeeventsource.h"
#include "eventpipe.h"
+#include "eventpipeinternal.h"
#endif //defined(FEATURE_EVENTSOURCE_XPLAT)
#ifdef FEATURE_PERFTRACING
#include "eventpipe.h"
+#include "eventpipeinternal.h"
#endif //FEATURE_PERFTRACING
#endif // CROSSGEN_MSCORLIB
diff --git a/src/vm/mscorlib.h b/src/vm/mscorlib.h
index aea2197f9a..d2a0ad61fd 100644
--- a/src/vm/mscorlib.h
+++ b/src/vm/mscorlib.h
@@ -320,6 +320,12 @@ DEFINE_FIELD(ENC_HELPER, OBJECT_REFERENCE, _objectReference)
DEFINE_CLASS(ENCODING, Text, Encoding)
+DEFINE_CLASS(RUNE, Text, Rune)
+
+#ifdef FEATURE_UTF8STRING
+DEFINE_CLASS(CHAR8, System, Char8)
+#endif // FEATURE_UTF8STRING
+
DEFINE_CLASS(ENUM, System, Enum)
DEFINE_CLASS(ENVIRONMENT, System, Environment)
@@ -335,7 +341,6 @@ DEFINE_CLASS(EVENT_HANDLERGENERIC, System, EventHandler`1)
DEFINE_CLASS(EVENT_INFO, Reflection, EventInfo)
DEFINE_CLASS_U(System, Exception, ExceptionObject)
-DEFINE_FIELD_U(_className, ExceptionObject, _className)
DEFINE_FIELD_U(_exceptionMethod, ExceptionObject, _exceptionMethod)
DEFINE_FIELD_U(_message, ExceptionObject, _message)
DEFINE_FIELD_U(_data, ExceptionObject, _data)
@@ -350,7 +355,6 @@ DEFINE_FIELD_U(_dynamicMethods, ExceptionObject, _dynamicMethods)
DEFINE_FIELD_U(_xptrs, ExceptionObject, _xptrs)
DEFINE_FIELD_U(_HResult, ExceptionObject, _HResult)
DEFINE_FIELD_U(_xcode, ExceptionObject, _xcode)
-DEFINE_FIELD_U(_remoteStackIndex, ExceptionObject, _remoteStackIndex)
DEFINE_FIELD_U(_ipForWatsonBuckets,ExceptionObject, _ipForWatsonBuckets)
DEFINE_CLASS(EXCEPTION, System, Exception)
DEFINE_METHOD(EXCEPTION, GET_CLASS_NAME, GetClassName, IM_RetStr)
@@ -488,9 +492,7 @@ DEFINE_CLASS(LCID_CONVERSION_TYPE, Interop, LCIDConversionAttrib
DEFINE_CLASS(MARSHAL, Interop, Marshal)
#ifdef FEATURE_COMINTEROP
-DEFINE_METHOD(MARSHAL, INITIALIZE_WRAPPER_FOR_WINRT, InitializeWrapperForWinRT, SM_Obj_RefIntPtr_RetVoid)
DEFINE_METHOD(MARSHAL, GET_HR_FOR_EXCEPTION, GetHRForException, SM_Exception_RetInt)
-DEFINE_METHOD(MARSHAL, GET_HR_FOR_EXCEPTION_WINRT, GetHRForException_WinRT, SM_Exception_RetInt)
#endif // FEATURE_COMINTEROP
DEFINE_METHOD(MARSHAL, GET_FUNCTION_POINTER_FOR_DELEGATE, GetFunctionPointerForDelegate, SM_Delegate_RetIntPtr)
DEFINE_METHOD(MARSHAL, GET_DELEGATE_FOR_FUNCTION_POINTER, GetDelegateForFunctionPointer, SM_IntPtr_Type_RetDelegate)
@@ -698,6 +700,7 @@ DEFINE_METHOD(RUNTIME_HELPERS, PREPARE_CONSTRAINED_REGIONS, PrepareConstrai
DEFINE_METHOD(RUNTIME_HELPERS, PREPARE_CONSTRAINED_REGIONS_NOOP, PrepareConstrainedRegionsNoOP, SM_RetVoid)
DEFINE_METHOD(RUNTIME_HELPERS, EXECUTE_BACKOUT_CODE_HELPER, ExecuteBackoutCodeHelper, SM_Obj_Obj_Bool_RetVoid)
DEFINE_METHOD(RUNTIME_HELPERS, IS_REFERENCE_OR_CONTAINS_REFERENCES, IsReferenceOrContainsReferences, NoSig)
+DEFINE_METHOD(RUNTIME_HELPERS, IS_BITWISE_EQUATABLE, IsBitwiseEquatable, NoSig)
DEFINE_CLASS(JIT_HELPERS, CompilerServices, JitHelpers)
DEFINE_METHOD(JIT_HELPERS, ENUM_EQUALS, EnumEquals, NoSig)
@@ -815,6 +818,17 @@ DEFINE_METHOD(STRING, WCSLEN, wcslen,
DEFINE_METHOD(STRING, STRLEN, strlen, SM_PtrByte_RetInt)
DEFINE_PROPERTY(STRING, LENGTH, Length, Int)
+#ifdef FEATURE_UTF8STRING
+DEFINE_CLASS(UTF8_STRING, System, Utf8String)
+DEFINE_METHOD(UTF8_STRING, CTORF_READONLYSPANOFBYTE,Ctor, IM_ReadOnlySpanOfByte_RetUtf8Str)
+DEFINE_METHOD(UTF8_STRING, CTORF_READONLYSPANOFCHAR,Ctor, IM_ReadOnlySpanOfChar_RetUtf8Str)
+DEFINE_METHOD(UTF8_STRING, CTORF_BYTEARRAY_START_LEN,Ctor, IM_ArrByte_Int_Int_RetUtf8Str)
+DEFINE_METHOD(UTF8_STRING, CTORF_BYTEPTR, Ctor, IM_PtrByte_RetUtf8Str)
+DEFINE_METHOD(UTF8_STRING, CTORF_CHARARRAY_START_LEN,Ctor, IM_ArrChar_Int_Int_RetUtf8Str)
+DEFINE_METHOD(UTF8_STRING, CTORF_CHARPTR, Ctor, IM_PtrChar_RetUtf8Str)
+DEFINE_METHOD(UTF8_STRING, CTORF_STRING, Ctor, IM_String_RetUtf8Str)
+#endif // FEATURE_UTF8STRING
+
DEFINE_CLASS(STRING_BUILDER, Text, StringBuilder)
DEFINE_PROPERTY(STRING_BUILDER, LENGTH, Length, Int)
DEFINE_PROPERTY(STRING_BUILDER, CAPACITY, Capacity, Int)
@@ -920,6 +934,8 @@ DEFINE_METHOD(BUFFER, MEMCPY, Memcpy,
#ifdef FEATURE_COMINTEROP
DEFINE_CLASS(WINDOWSRUNTIMEMARSHAL, WinRT, WindowsRuntimeMarshal)
+DEFINE_METHOD(WINDOWSRUNTIMEMARSHAL, GET_HR_FOR_EXCEPTION, GetHRForException, SM_Exception_RetInt)
+DEFINE_METHOD(WINDOWSRUNTIMEMARSHAL, INITIALIZE_WRAPPER, InitializeWrapper, SM_Obj_RefIntPtr_RetVoid)
#ifdef FEATURE_COMINTEROP_WINRT_MANAGED_ACTIVATION
DEFINE_METHOD(WINDOWSRUNTIMEMARSHAL, GET_ACTIVATION_FACTORY_FOR_TYPE, GetActivationFactoryForType, SM_Type_RetIntPtr)
#endif // FEATURE_COMINTEROP_WINRT_MANAGED_ACTIVATION
diff --git a/src/vm/mtypes.h b/src/vm/mtypes.h
index af21d267c7..5f75eeb6e6 100644
--- a/src/vm/mtypes.h
+++ b/src/vm/mtypes.h
@@ -88,7 +88,7 @@ DEFINE_MARSHALER_TYPE(MARSHAL_TYPE_VALUECLASS, ValueClassMa
DEFINE_MARSHALER_TYPE(MARSHAL_TYPE_REFERENCECUSTOMMARSHALER, ReferenceCustomMarshaler, false)
DEFINE_MARSHALER_TYPE(MARSHAL_TYPE_ARGITERATOR, ArgIteratorMarshaler, false)
-
+DEFINE_MARSHALER_TYPE(MARSHAL_TYPE_BLITTABLEVALUECLASSWITHCOPYCTOR, BlittableValueClassWithCopyCtorMarshaler, false)
#ifdef FEATURE_COMINTEROP
DEFINE_MARSHALER_TYPE(MARSHAL_TYPE_OBJECT, ObjectMarshaler, false)
diff --git a/src/vm/object.h b/src/vm/object.h
index 6bc3a74471..b4b2363014 100644
--- a/src/vm/object.h
+++ b/src/vm/object.h
@@ -35,7 +35,10 @@ void ErectWriteBarrierForMT(MethodTable **dst, MethodTable *ref);
* | sync block index, which is at a negative offset
* |
* +-- code:StringObject - String objects are specialized objects for string
- * | storage/retrieval for higher performance
+ * | storage/retrieval for higher performance (UCS-2 / UTF-16 data)
+ * |
+ * +-- code:Utf8StringObject - String objects are specialized objects for string
+ * | storage/retrieval for higher performance (UTF-8 data)
* |
* +-- BaseObjectWithCachedData - Object Plus one object field for caching.
* | |
@@ -870,6 +873,9 @@ typedef DPTR(UPTRArray) PTR_UPTRArray;
typedef DPTR(PTRArray) PTR_PTRArray;
class StringObject;
+#ifdef FEATURE_UTF8STRING
+class Utf8StringObject;
+#endif // FEATURE_UTF8STRING
#ifdef USE_CHECKED_OBJECTREFS
typedef REF<ArrayBase> BASEARRAYREF;
@@ -888,6 +894,9 @@ typedef REF<UPTRArray> UPTRARRAYREF;
typedef REF<CHARArray> CHARARRAYREF;
typedef REF<PTRArray> PTRARRAYREF; // Warning: Use PtrArray only for single dimensional arrays, not multidim arrays.
typedef REF<StringObject> STRINGREF;
+#ifdef FEATURE_UTF8STRING
+typedef REF<Utf8StringObject> UTF8STRINGREF;
+#endif // FEATURE_UTF8STRING
#else // USE_CHECKED_OBJECTREFS
@@ -907,6 +916,9 @@ typedef PTR_UPTRArray UPTRARRAYREF;
typedef PTR_CHARArray CHARARRAYREF;
typedef PTR_PTRArray PTRARRAYREF; // Warning: Use PtrArray only for single dimensional arrays, not multidim arrays.
typedef PTR_StringObject STRINGREF;
+#ifdef FEATURE_UTF8STRING
+typedef PTR_Utf8StringObject UTF8STRINGREF;
+#endif // FEATURE_UTF8STRING
#endif // USE_CHECKED_OBJECTREFS
@@ -1199,6 +1211,56 @@ public:
};
+#ifdef FEATURE_UTF8STRING
+class Utf8StringObject : public Object
+{
+#ifdef DACCESS_COMPILE
+ friend class ClrDataAccess;
+#endif
+
+private:
+ DWORD m_StringLength;
+ BYTE m_FirstChar;
+
+public:
+ VOID SetLength(DWORD len) { LIMITED_METHOD_CONTRACT; _ASSERTE(len >= 0); m_StringLength = len; }
+
+protected:
+ Utf8StringObject() { LIMITED_METHOD_CONTRACT; }
+ ~Utf8StringObject() { LIMITED_METHOD_CONTRACT; }
+
+public:
+
+ /*=================RefInterpretGetStringValuesDangerousForGC======================
+ **N.B.: This perfoms no range checking and relies on the caller to have done this.
+ **Args: (IN)ref -- the Utf8String to be interpretted.
+ ** (OUT)chars -- a pointer to the characters in the buffer.
+ ** (OUT)length -- a pointer to the length of the buffer.
+ **Returns: void.
+ **Exceptions: None.
+ ==============================================================================*/
+ // !!!! If you use this function, you have to be careful because chars is a pointer
+ // !!!! to the data buffer of ref. If GC happens after this call, you need to make
+ // !!!! sure that you have a pin handle on ref, or use GCPROTECT_BEGINPINNING on ref.
+ void RefInterpretGetStringValuesDangerousForGC(__deref_out_ecount(*length + 1) CHAR **chars, int *length) {
+ WRAPPER_NO_CONTRACT;
+
+ _ASSERTE(GetGCSafeMethodTable() == g_pUtf8StringClass);
+ *length = GetStringLength();
+ *chars = GetBuffer();
+#ifdef _DEBUG
+ EnableStressHeapHelper();
+#endif
+ }
+
+ DWORD GetStringLength() { LIMITED_METHOD_DAC_CONTRACT; return( m_StringLength );}
+ CHAR* GetBuffer() { LIMITED_METHOD_CONTRACT; _ASSERTE(this != nullptr); return (CHAR*)( dac_cast<TADDR>(this) + offsetof(Utf8StringObject, m_FirstChar) ); }
+
+ static DWORD GetBaseSize();
+ static SIZE_T GetSize(DWORD stringLength);
+};
+#endif // FEATURE_UTF8STRING
+
// This is the Method version of the Reflection object.
// A Method has adddition information.
// m_pMD - A pointer to the actual MethodDesc of the method.
@@ -2647,7 +2709,6 @@ public:
// If you modify the order of these fields, make sure to update the definition in
// BCL for this object.
private:
- STRINGREF _className; //Needed for serialization.
OBJECTREF _exceptionMethod; //Needed for serialization.
STRINGREF _message;
OBJECTREF _data;
@@ -2660,13 +2721,10 @@ private:
PTRARRAYREF _dynamicMethods;
STRINGREF _source; // Mainly used by VB.
- IN_WIN64(void* _xptrs;)
- IN_WIN64(UINT_PTR _ipForWatsonBuckets;) // Contains the IP of exception for watson bucketing
- INT32 _remoteStackIndex;
- INT32 _HResult;
- IN_WIN32(void* _xptrs;)
+ UINT_PTR _ipForWatsonBuckets; // Contains the IP of exception for watson bucketing
+ void* _xptrs;
INT32 _xcode;
- IN_WIN32(UINT_PTR _ipForWatsonBuckets;) // Contains the IP of exception for watson bucketing
+ INT32 _HResult;
};
// Defined in Contracts.cs
@@ -2695,10 +2753,9 @@ public:
private:
// keep these in sync with ndp/clr/src/bcl/system/diagnostics/contracts/contractsbcl.cs
- IN_WIN64(INT32 _Kind;)
STRINGREF _UserMessage;
STRINGREF _Condition;
- IN_WIN32(INT32 _Kind;)
+ INT32 _Kind;
};
#include "poppack.h"
diff --git a/src/vm/object.inl b/src/vm/object.inl
index 9652909250..ebf9d364c9 100644
--- a/src/vm/object.inl
+++ b/src/vm/object.inl
@@ -71,6 +71,22 @@ __forceinline /*static*/ SIZE_T StringObject::GetSize(DWORD strLen)
return GetBaseSize() + strLen * sizeof(WCHAR);
}
+#ifdef FEATURE_UTF8STRING
+__forceinline /*static*/ DWORD Utf8StringObject::GetBaseSize()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return OBJECT_BASESIZE + sizeof(DWORD) /* length */ + sizeof(BYTE) /* null terminator */;
+}
+
+__forceinline /*static*/ SIZE_T Utf8StringObject::GetSize(DWORD strLen)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return GetBaseSize() + strLen;
+}
+#endif // FEATURE_UTF8STRING
+
#ifdef DACCESS_COMPILE
inline void Object::EnumMemoryRegions(void)
diff --git a/src/vm/proftoeeinterfaceimpl.cpp b/src/vm/proftoeeinterfaceimpl.cpp
index 854288847e..663b82f403 100644
--- a/src/vm/proftoeeinterfaceimpl.cpp
+++ b/src/vm/proftoeeinterfaceimpl.cpp
@@ -9661,12 +9661,12 @@ HRESULT ProfToEEInterfaceImpl::ApplyMetaData(
CONTRACTL
{
NOTHROW;
- GC_NOTRIGGER;
+ GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
- PROFILER_TO_CLR_ENTRYPOINT_SYNC_EX(kP2EEAllowableAfterAttach, (LF_CORPROF, LL_INFO1000, "**PROF: ApplyMetaData.\n"));
+ PROFILER_TO_CLR_ENTRYPOINT_SYNC_EX(kP2EEAllowableAfterAttach | kP2EETriggers, (LF_CORPROF, LL_INFO1000, "**PROF: ApplyMetaData.\n"));
if (moduleId == NULL)
{
diff --git a/src/vm/reflectioninvocation.cpp b/src/vm/reflectioninvocation.cpp
index 1f8aa04593..954d6ae267 100644
--- a/src/vm/reflectioninvocation.cpp
+++ b/src/vm/reflectioninvocation.cpp
@@ -1001,6 +1001,7 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod,
// Skip the activation optimization for remoting because of remoting proxy is not always activated.
// It would be nice to clean this up and get remoting to always activate methodtable behind the proxy.
BOOL fForceActivationForRemoting = FALSE;
+ BOOL fCtorOfVariableSizedObject = FALSE;
if (fConstructor)
{
@@ -1018,7 +1019,8 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod,
MethodTable * pMT = ownerType.AsMethodTable();
{
- if (pMT != g_pStringClass)
+ fCtorOfVariableSizedObject = pMT->HasComponentSize();
+ if (!fCtorOfVariableSizedObject)
gc.retVal = pMT->Allocate();
}
}
@@ -1324,7 +1326,11 @@ FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod,
if (fConstructor)
{
// We have a special case for Strings...The object is returned...
- if (ownerType == TypeHandle(g_pStringClass)) {
+ if (ownerType == TypeHandle(g_pStringClass)
+#ifdef FEATURE_UTF8STRING
+ || ownerType == TypeHandle(g_pUtf8StringClass)
+#endif // FEATURE_UTF8STRING
+ ) {
PVOID pReturnValue = &callDescrData.returnValue;
gc.retVal = *(OBJECTREF *)pReturnValue;
}
@@ -2590,8 +2596,12 @@ FCIMPL1(Object*, ReflectionSerialization::GetUninitializedObject, ReflectClassBa
MethodTable *pMT = type.GetMethodTable();
PREFIX_ASSUME(pMT != NULL);
- //We don't allow unitialized strings.
- if (pMT == g_pStringClass) {
+ //We don't allow unitialized Strings or Utf8Strings.
+ if (pMT == g_pStringClass
+#ifdef FEATURE_UTF8STRING
+ || pMT == g_pUtf8StringClass
+#endif // FEATURE_UTF8STRING
+ ) {
COMPlusThrow(kArgumentException, W("Argument_NoUninitializedStrings"));
}
diff --git a/src/vm/threadpoolrequest.h b/src/vm/threadpoolrequest.h
index 17df7a790f..0eb037523e 100644
--- a/src/vm/threadpoolrequest.h
+++ b/src/vm/threadpoolrequest.h
@@ -163,7 +163,7 @@ public:
private:
ADID m_id;
TPIndex m_index;
- DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) struct {
+ struct DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) {
BYTE m_padding1[MAX_CACHE_LINE_SIZE - sizeof(LONG)];
// Only use with VolatileLoad+VolatileStore+FastInterlockCompareExchange
LONG m_numRequestsPending;
@@ -256,7 +256,7 @@ public:
private:
SpinLock m_lock;
ULONG m_NumRequests;
- DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) struct {
+ struct DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) {
BYTE m_padding1[MAX_CACHE_LINE_SIZE - sizeof(LONG)];
// Only use with VolatileLoad+VolatileStore+FastInterlockCompareExchange
LONG m_outstandingThreadRequestCount;
diff --git a/src/vm/vars.cpp b/src/vm/vars.cpp
index 179acda8af..8b329d4c2e 100644
--- a/src/vm/vars.cpp
+++ b/src/vm/vars.cpp
@@ -61,6 +61,9 @@ GPTR_IMPL(MethodTable, g_pObjectClass);
GPTR_IMPL(MethodTable, g_pRuntimeTypeClass);
GPTR_IMPL(MethodTable, g_pCanonMethodTableClass); // System.__Canon
GPTR_IMPL(MethodTable, g_pStringClass);
+#ifdef FEATURE_UTF8STRING
+GPTR_IMPL(MethodTable, g_pUtf8StringClass);
+#endif // FEATURE_UTF8STRING
GPTR_IMPL(MethodTable, g_pArrayClass);
GPTR_IMPL(MethodTable, g_pSZArrayHelperClass);
GPTR_IMPL(MethodTable, g_pNullableClass);
diff --git a/src/vm/vars.hpp b/src/vm/vars.hpp
index 91ad42a91c..d8ffc60e25 100644
--- a/src/vm/vars.hpp
+++ b/src/vm/vars.hpp
@@ -79,6 +79,9 @@ class LoaderHeap;
class IGCHeap;
class Object;
class StringObject;
+#ifdef FEATURE_UTF8STRING
+class Utf8StringObject;
+#endif // FEATURE_UTF8STRING
class ArrayClass;
class MethodTable;
class MethodDesc;
@@ -313,6 +316,10 @@ class REF : public OBJECTREF
#define OBJECTREFToObject(objref) ((objref).operator-> ())
#define ObjectToSTRINGREF(obj) (STRINGREF(obj))
#define STRINGREFToObject(objref) (*( (StringObject**) &(objref) ))
+#ifdef FEATURE_UTF8STRING
+#define ObjectToUTF8STRINGREF(obj) (UTF8STRINGREF(obj))
+#define UTF8STRINGREFToObject(objref) (*( (Utf8StringObject**) &(objref) ))
+#endif // FEATURE_UTF8STRING
#else // _DEBUG_IMPL
@@ -323,6 +330,10 @@ class REF : public OBJECTREF
#define OBJECTREFToObject(objref) ((PTR_Object) (objref))
#define ObjectToSTRINGREF(obj) ((PTR_StringObject) (obj))
#define STRINGREFToObject(objref) ((PTR_StringObject) (objref))
+#ifdef FEATURE_UTF8STRING
+#define ObjectToUTF8STRINGREF(obj) ((PTR_Utf8StringObject) (obj))
+#define UTF8STRINGREFToObject(objref) ((PTR_Utf8StringObject) (objref))
+#endif // FEATURE_UTF8STRING
#endif // _DEBUG_IMPL
@@ -363,6 +374,9 @@ GPTR_DECL(MethodTable, g_pObjectClass);
GPTR_DECL(MethodTable, g_pRuntimeTypeClass);
GPTR_DECL(MethodTable, g_pCanonMethodTableClass); // System.__Canon
GPTR_DECL(MethodTable, g_pStringClass);
+#ifdef FEATURE_UTF8STRING
+GPTR_DECL(MethodTable, g_pUtf8StringClass);
+#endif // FEATURE_UTF8STRING
GPTR_DECL(MethodTable, g_pArrayClass);
GPTR_DECL(MethodTable, g_pSZArrayHelperClass);
GPTR_DECL(MethodTable, g_pNullableClass);
diff --git a/src/zap/zapimport.cpp b/src/zap/zapimport.cpp
index d84ea79e8a..9bdc1d9499 100644
--- a/src/zap/zapimport.cpp
+++ b/src/zap/zapimport.cpp
@@ -1831,7 +1831,7 @@ ZapImport * ZapImportTable::GetDictionaryLookupCell(CORCOMPILE_FIXUP_BLOB_KIND k
ThrowHR(E_NOTIMPL);
}
- _ASSERTE(((DWORD)pResolvedToken->tokenContext & 1) == 0);
+ _ASSERTE(((DWORD)(SIZE_T)pResolvedToken->tokenContext & 1) == 0);
return GetImportForSignature<ZapDynamicHelperCell, ZapNodeType_DynamicHelperCell>((void*)pResolvedToken->tokenContext, &sigBuilder);
}
@@ -1894,12 +1894,12 @@ public:
ReadyToRunHelper GetReadyToRunHelper()
{
- return (ReadyToRunHelper)((DWORD)GetHandle() & ~READYTORUN_HELPER_FLAG_VSD);
+ return (ReadyToRunHelper)((DWORD)(SIZE_T)GetHandle() & ~READYTORUN_HELPER_FLAG_VSD);
}
DWORD GetSectionIndex()
{
- return (DWORD)GetHandle2();
+ return (DWORD)(SIZE_T)GetHandle2();
}
BOOL IsDelayLoadHelper()
@@ -1919,7 +1919,7 @@ public:
BOOL IsVSD()
{
- return ((DWORD)GetHandle() & READYTORUN_HELPER_FLAG_VSD) != 0;
+ return ((DWORD)(SIZE_T)GetHandle() & READYTORUN_HELPER_FLAG_VSD) != 0;
}
BOOL IsLazyHelper()
diff --git a/tests/CoreFX/CoreFX.issues.json b/tests/CoreFX/CoreFX.issues.json
index 3db01ef4e1..28eb92937a 100644
--- a/tests/CoreFX/CoreFX.issues.json
+++ b/tests/CoreFX/CoreFX.issues.json
@@ -1254,6 +1254,10 @@
"reason": "Needs updates for XUnit 2.4"
},
{
+ "name": "System.Tests.ArraySegment_Tests.CopyTo_Invalid",
+ "reason": "Needs parameter name updated to 'destination'."
+ },
+ {
"name": "System.Tests.TimeSpanTests.Parse_Invalid",
"reason": "Temporary disabling till merging the PR https://github.com/dotnet/corefx/pull/34561"
},
diff --git a/tests/arm64/corefx_test_exclusions.txt b/tests/arm64/corefx_test_exclusions.txt
index 19f9ef54a6..cb2939917f 100644
--- a/tests/arm64/corefx_test_exclusions.txt
+++ b/tests/arm64/corefx_test_exclusions.txt
@@ -1,12 +1,7 @@
-Invariant.Tests
+Microsoft.Win32.SystemEvents.Tests # https://github.com/dotnet/coreclr/issues/22442 -- timeout
System.ComponentModel.Composition.Tests # https://github.com/dotnet/coreclr/issues/18913
-System.Diagnostics.Process.Tests # https://github.com/dotnet/coreclr/issues/16001
-System.Drawing.Common.Tests # https://github.com/dotnet/coreclr/issues/18886
-System.Management.Tests # https://github.com/dotnet/coreclr/issues/18886
+System.Drawing.Common.Tests # https://github.com/dotnet/corefx/issues/35424
+System.Management.Tests # https://github.com/dotnet/corefx/issues/34030
System.Net.HttpListener.Tests # https://github.com/dotnet/coreclr/issues/17584
-System.Numerics.Vectors.Tests # https://github.com/dotnet/coreclr/issues/18886
-System.Runtime.InteropServices.RuntimeInformation.Tests # VM assert -- https://github.com/dotnet/coreclr/issues/18886
-System.Runtime.Serialization.Formatters.Tests # long running? https://github.com/dotnet/coreclr/issues/18886
System.Runtime.Tests # https://github.com/dotnet/coreclr/issues/18914
System.Text.RegularExpressions.Tests # https://github.com/dotnet/coreclr/issues/18912 -- timeout -- JitMinOpts only
-System.ValueTuple.Tests
diff --git a/tests/issues.targets b/tests/issues.targets
index a206167a09..96c3d5241c 100644
--- a/tests/issues.targets
+++ b/tests/issues.targets
@@ -47,9 +47,6 @@
<ExcludeList Include="$(XunitTestBinBase)/JIT/Regression/JitBlue/GitHub_11408/GitHub_11408/*">
<Issue>11408</Issue>
</ExcludeList>
- <ExcludeList Include="$(XunitTestBinBase)/JIT/Regression/JitBlue/GitHub_20958/GitHub_20958/*">
- <Issue>22410</Issue>
- </ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/baseservices/exceptions/StackTracePreserve/StackTracePreserveTests/*">
<Issue>20322</Issue>
</ExcludeList>
@@ -283,7 +280,7 @@
<Issue>22974</Issue>
</ExcludeList>
</ItemGroup>
-
+
<!-- Windows x64 specific excludes -->
<ItemGroup Condition="'$(XunitTestBinBase)' != '' and '$(BuildArch)' == 'x64' and '$(TargetsWindows)' == 'true'">
<ExcludeList Include="$(XunitTestBinBase)/JIT/Regression/JitBlue/GitHub_18056/Bool_And_Op_cs_do/*">
@@ -458,6 +455,15 @@
<ExcludeList Include="$(XunitTestBinBase)/JIT/Directed/arglist/vararg/*">
<Issue>Needs triage</Issue>
</ExcludeList>
+ <ExcludeList Include="$(XunitTestBinBase)/Interop/IJW/CopyConstructorMarshaler/CopyConstructorMarshaler/*">
+ <Issue>Needs triage</Issue>
+ </ExcludeList>
+ <ExcludeList Include="$(XunitTestBinBase)/Interop/IJW/FixupCallsHostWhenLoaded/FixupCallsHostWhenLoaded/*">
+ <Issue>Needs triage</Issue>
+ </ExcludeList>
+ <ExcludeList Include="$(XunitTestBinBase)/Interop/IJW/LoadIjwFromModuleHandle/LoadIjwFromModuleHandle/*">
+ <Issue>Needs triage</Issue>
+ </ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/Interop/IJW/ManagedCallingNative/ManagedCallingNative/*">
<Issue>Needs triage</Issue>
</ExcludeList>
diff --git a/tests/src/GC/API/GC/Collect.csproj b/tests/src/GC/API/GC/Collect.csproj
index 6a07e5527a..36a935e502 100644
--- a/tests/src/GC/API/GC/Collect.csproj
+++ b/tests/src/GC/API/GC/Collect.csproj
@@ -11,6 +11,7 @@
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
<CLRTestPriority>1</CLRTestPriority>
+ <GCStressIncompatible>true</GCStressIncompatible>
</PropertyGroup>
<!-- Default configurations to help VS understand the configurations -->
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
diff --git a/tests/src/Interop/CMakeLists.txt b/tests/src/Interop/CMakeLists.txt
index b4dec5750d..acf4c849db 100644
--- a/tests/src/Interop/CMakeLists.txt
+++ b/tests/src/Interop/CMakeLists.txt
@@ -83,11 +83,12 @@ if(WIN32)
add_subdirectory(COM/NativeClients/Primitives)
add_subdirectory(COM/NativeClients/Licensing)
add_subdirectory(COM/NativeClients/DefaultInterfaces)
- add_subdirectory(IJW/FakeMscoree)
# IJW isn't supported on ARM64
if(NOT CLR_CMAKE_PLATFORM_ARCH_ARM64)
- add_subdirectory(IJW/ManagedCallingNative/IjwNativeDll)
- add_subdirectory(IJW/NativeCallingManaged/IjwNativeCallingManagedDll)
+ add_subdirectory(IJW/ijwhostmock)
+ add_subdirectory(IJW/IjwNativeDll)
+ add_subdirectory(IJW/IjwNativeCallingManagedDll)
+ add_subdirectory(IJW/CopyConstructorMarshaler)
endif()
endif(WIN32)
diff --git a/tests/src/Interop/IJW/CopyConstructorMarshaler/CMakeLists.txt b/tests/src/Interop/IJW/CopyConstructorMarshaler/CMakeLists.txt
new file mode 100644
index 0000000000..57726a4318
--- /dev/null
+++ b/tests/src/Interop/IJW/CopyConstructorMarshaler/CMakeLists.txt
@@ -0,0 +1,42 @@
+cmake_minimum_required (VERSION 2.6)
+project (CopyConstructorMarshaler)
+include_directories( ${INC_PLATFORM_DIR} )
+set(SOURCES IjwCopyConstructorMarshaler.cpp)
+
+if (WIN32)
+ # 4365 - signed/unsigned mismatch
+ add_compile_options(/wd4365)
+
+ # IJW
+ add_compile_options(/clr)
+
+ # IJW requires the CRT as a dll, not linked in
+ add_compile_options(/MD$<$<OR:$<CONFIG:Debug>,$<CONFIG:Checked>>:d>)
+
+ # CMake enables /RTC1 and /EHsc by default, but they're not compatible with /clr, so remove them
+ if(CMAKE_CXX_FLAGS_DEBUG MATCHES "/RTC1")
+ string(REPLACE "/RTC1" " " CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
+ endif()
+
+ if(CMAKE_CXX_FLAGS MATCHES "/EHsc")
+ string(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+ endif()
+
+ # IJW isn't compatible with CFG
+ if(CMAKE_CXX_FLAGS MATCHES "/guard:cf")
+ string(REPLACE "/guard:cf" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+ endif()
+
+ # IJW isn't compatible with GR-
+ if(CMAKE_CXX_FLAGS MATCHES "/GR-")
+ string(REPLACE "/GR-" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+ endif()
+
+endif()
+
+# add the shared library
+add_library (IjwCopyConstructorMarshaler SHARED ${SOURCES})
+target_link_libraries(IjwCopyConstructorMarshaler ${LINK_LIBRARIES_ADDITIONAL})
+
+# add the install targets
+install (TARGETS IjwCopyConstructorMarshaler DESTINATION bin)
diff --git a/tests/src/Interop/IJW/CopyConstructorMarshaler/CopyConstructorMarshaler.cs b/tests/src/Interop/IJW/CopyConstructorMarshaler/CopyConstructorMarshaler.cs
new file mode 100644
index 0000000000..d589cd4fe8
--- /dev/null
+++ b/tests/src/Interop/IJW/CopyConstructorMarshaler/CopyConstructorMarshaler.cs
@@ -0,0 +1,66 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using TestLibrary;
+
+namespace CopyConstructorMarshaler
+{
+ class CopyConstructorMarshaler
+ {
+ static int Main(string[] args)
+ {
+ if(Environment.OSVersion.Platform != PlatformID.Win32NT || TestLibrary.Utilities.IsWindows7)
+ {
+ return 100;
+ }
+
+ try
+ {
+ // Load a fake mscoree.dll to avoid starting desktop
+ LoadLibraryEx(Path.Combine(Environment.CurrentDirectory, "mscoree.dll"), IntPtr.Zero, 0);
+
+ Assembly ijwNativeDll = Assembly.Load("IjwCopyConstructorMarshaler");
+ Type testType = ijwNativeDll.GetType("TestClass");
+ object testInstance = Activator.CreateInstance(testType);
+ MethodInfo testMethod = testType.GetMethod("PInvokeNumCopies");
+
+ // PInvoke will copy twice. Once from argument to parameter, and once from the managed to native parameter.
+ Assert.AreEqual(2, (int)testMethod.Invoke(testInstance, null));
+
+ testMethod = testType.GetMethod("ReversePInvokeNumCopies");
+
+ // Reverse PInvoke will copy 3 times. Two are from the same paths as the PInvoke,
+ // and the third is from the reverse P/Invoke call.
+ Assert.AreEqual(3, (int)testMethod.Invoke(testInstance, null));
+
+ testMethod = testType.GetMethod("PInvokeNumCopiesDerivedType");
+
+ // PInvoke will copy twice. Once from argument to parameter, and once from the managed to native parameter.
+ Assert.AreEqual(2, (int)testMethod.Invoke(testInstance, null));
+
+ testMethod = testType.GetMethod("ReversePInvokeNumCopiesDerivedType");
+
+ // Reverse PInvoke will copy 3 times. Two are from the same paths as the PInvoke,
+ // and the third is from the reverse P/Invoke call.
+ Assert.AreEqual(3, (int)testMethod.Invoke(testInstance, null));
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(ex);
+ return 101;
+ }
+ return 100;
+ }
+
+ [DllImport("kernel32.dll")]
+ static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, int dwFlags);
+
+ [DllImport("kernel32.dll")]
+ static extern IntPtr GetModuleHandle(string lpModuleName);
+ }
+}
diff --git a/tests/src/Interop/IJW/CopyConstructorMarshaler/CopyConstructorMarshaler.csproj b/tests/src/Interop/IJW/CopyConstructorMarshaler/CopyConstructorMarshaler.csproj
new file mode 100644
index 0000000000..b1d38a911b
--- /dev/null
+++ b/tests/src/Interop/IJW/CopyConstructorMarshaler/CopyConstructorMarshaler.csproj
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Interop.settings.targets))\Interop.settings.targets" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <AssemblyName>CopyConstructorMarshaler</AssemblyName>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{49D1D482-E783-4CA9-B6BA-A9714BF81036}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+
+ <!-- IJW is Windows-only -->
+ <!-- Test unsupported outside of windows -->
+ <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows>
+ <DisableProjectBuild Condition="'$(TargetsUnix)' == 'true'">true</DisableProjectBuild>
+
+ <!-- IJW is not supported on ARM64 -->
+ <DisableProjectBuild Condition="'$(Platform)' == 'arm64'">true</DisableProjectBuild>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+ </PropertyGroup>
+ <PropertyGroup>
+ <CopyDebugCRTDllsToOutputDirectory>true</CopyDebugCRTDllsToOutputDirectory>
+ </PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="CopyConstructorMarshaler.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="CMakeLists.txt" />
+ <ProjectReference Include="../ijwhostmock/CMakeLists.txt" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/tests/src/Interop/IJW/CopyConstructorMarshaler/IjwCopyConstructorMarshaler.cpp b/tests/src/Interop/IJW/CopyConstructorMarshaler/IjwCopyConstructorMarshaler.cpp
new file mode 100644
index 0000000000..b82c33b349
--- /dev/null
+++ b/tests/src/Interop/IJW/CopyConstructorMarshaler/IjwCopyConstructorMarshaler.cpp
@@ -0,0 +1,106 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#pragma managed
+class A
+{
+ int copyCount;
+
+public:
+ A()
+ :copyCount(0)
+ {};
+
+ A(A& other)
+ :copyCount(other.copyCount + 1)
+ {}
+
+ int GetCopyCount()
+ {
+ return copyCount;
+ }
+};
+
+class B : public A
+{
+ int bCopyCount;
+
+public:
+ B()
+ :A(),
+ bCopyCount(0)
+ {};
+
+ B(B& other)
+ :A(other),
+ bCopyCount(other.bCopyCount + 1)
+ {}
+
+ int GetBCopyCount()
+ {
+ return bCopyCount;
+ }
+};
+
+int Managed_GetCopyCount(A a)
+{
+ return a.GetCopyCount();
+}
+
+int Managed_GetCopyCount(B b)
+{
+ return b.GetBCopyCount();
+}
+
+#pragma unmanaged
+
+int GetCopyCount(A a)
+{
+ return a.GetCopyCount();
+}
+
+int GetCopyCount_ViaManaged(A a)
+{
+ return Managed_GetCopyCount(a);
+}
+
+int GetCopyCount(B b)
+{
+ return b.GetBCopyCount();
+}
+
+int GetCopyCount_ViaManaged(B b)
+{
+ return Managed_GetCopyCount(b);
+}
+
+#pragma managed
+
+public ref class TestClass
+{
+public:
+ int PInvokeNumCopies()
+ {
+ A a;
+ return GetCopyCount(a);
+ }
+
+ int PInvokeNumCopiesDerivedType()
+ {
+ B b;
+ return GetCopyCount(b);
+ }
+
+ int ReversePInvokeNumCopies()
+ {
+ A a;
+ return GetCopyCount_ViaManaged(a);
+ }
+
+ int ReversePInvokeNumCopiesDerivedType()
+ {
+ B b;
+ return GetCopyCount_ViaManaged(b);
+ }
+};
diff --git a/tests/src/Interop/IJW/FakeMscoree/mscoree.cpp b/tests/src/Interop/IJW/FakeMscoree/mscoree.cpp
deleted file mode 100644
index 99319ec28b..0000000000
--- a/tests/src/Interop/IJW/FakeMscoree/mscoree.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-#include <windows.h>
-
-#ifndef DLLEXPORT
-#ifdef _MSC_VER
-#define DLLEXPORT __declspec(dllexport)
-#else
-#define DLLEXPORT __attribute__((visibility("default")))
-#endif
-#endif
-
-// Entrypoint jumped to by IJW dlls when their dllmain is called
-extern "C" DLLEXPORT BOOL WINAPI _CorDllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved)
-{
- return TRUE;
-}
diff --git a/tests/src/Interop/IJW/FakeMscoree/mscoree.def b/tests/src/Interop/IJW/FakeMscoree/mscoree.def
deleted file mode 100644
index 9279b0364b..0000000000
--- a/tests/src/Interop/IJW/FakeMscoree/mscoree.def
+++ /dev/null
@@ -1,3 +0,0 @@
-LIBRARY MSCOREE
-EXPORTS
- _CorDllMain \ No newline at end of file
diff --git a/tests/src/Interop/IJW/FixupCallsHostWhenLoaded/FixupCallsHostWhenLoaded.cs b/tests/src/Interop/IJW/FixupCallsHostWhenLoaded/FixupCallsHostWhenLoaded.cs
new file mode 100644
index 0000000000..cfc5a9a73d
--- /dev/null
+++ b/tests/src/Interop/IJW/FixupCallsHostWhenLoaded/FixupCallsHostWhenLoaded.cs
@@ -0,0 +1,53 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using TestLibrary;
+
+namespace FixupCallsHostWhenLoaded
+{
+ class FixupCallsHostWhenLoaded
+ {
+ static int Main(string[] args)
+ {
+ // Disable running on Windows 7 until IJW activation work is complete.
+ if(Environment.OSVersion.Platform != PlatformID.Win32NT || TestLibrary.Utilities.IsWindows7)
+ {
+ return 100;
+ }
+
+ try
+ {
+ // Load a fake mscoree.dll to avoid starting desktop
+ IntPtr ijwHost = NativeLibrary.Load(Path.Combine(Environment.CurrentDirectory, "mscoree.dll"));
+
+ WasModuleVTableQueriedDelegate wasModuleVTableQueried = Marshal.GetDelegateForFunctionPointer<WasModuleVTableQueriedDelegate>(NativeLibrary.GetExport(ijwHost, "WasModuleVTableQueried"));
+
+ // Load IJW via reflection
+ Assembly.Load("IjwNativeDll");
+
+ IntPtr ijwModuleHandle = GetModuleHandle("IjwNativeDll.dll");
+
+ Assert.AreNotEqual(IntPtr.Zero, ijwModuleHandle);
+ Assert.IsTrue(wasModuleVTableQueried(ijwModuleHandle));
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e);
+ return 101;
+ }
+
+ return 100;
+ }
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ delegate bool WasModuleVTableQueriedDelegate(IntPtr handle);
+
+ [DllImport("kernel32.dll")]
+ static extern IntPtr GetModuleHandle(string lpModuleName);
+ }
+}
diff --git a/tests/src/Interop/IJW/FixupCallsHostWhenLoaded/FixupCallsHostWhenLoaded.csproj b/tests/src/Interop/IJW/FixupCallsHostWhenLoaded/FixupCallsHostWhenLoaded.csproj
new file mode 100644
index 0000000000..20dc72f397
--- /dev/null
+++ b/tests/src/Interop/IJW/FixupCallsHostWhenLoaded/FixupCallsHostWhenLoaded.csproj
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Interop.settings.targets))\Interop.settings.targets" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <AssemblyName>FixupCallsHostWhenLoaded</AssemblyName>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{49D1D482-E783-4CA9-B6BA-A9714BF81036}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+
+ <!-- IJW is Windows-only -->
+ <!-- Test unsupported outside of windows -->
+ <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows>
+ <DisableProjectBuild Condition="'$(TargetsUnix)' == 'true'">true</DisableProjectBuild>
+
+ <!-- IJW is not supported on ARM64 -->
+ <DisableProjectBuild Condition="'$(Platform)' == 'arm64'">true</DisableProjectBuild>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+ </PropertyGroup>
+ <PropertyGroup>
+ <CopyDebugCRTDllsToOutputDirectory>true</CopyDebugCRTDllsToOutputDirectory>
+ </PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="FixupCallsHostWhenLoaded.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="../IjwNativeDll/CMakeLists.txt" />
+ <ProjectReference Include="../ijwhostmock/CMakeLists.txt" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/tests/src/Interop/IJW/NativeCallingManaged/IjwNativeCallingManagedDll/CMakeLists.txt b/tests/src/Interop/IJW/IjwNativeCallingManagedDll/CMakeLists.txt
index c8b0edcb83..c8b0edcb83 100644
--- a/tests/src/Interop/IJW/NativeCallingManaged/IjwNativeCallingManagedDll/CMakeLists.txt
+++ b/tests/src/Interop/IJW/IjwNativeCallingManagedDll/CMakeLists.txt
diff --git a/tests/src/Interop/IJW/NativeCallingManaged/IjwNativeCallingManagedDll/IjwNativeCallingManagedDll.cpp b/tests/src/Interop/IJW/IjwNativeCallingManagedDll/IjwNativeCallingManagedDll.cpp
index 9ba7e3ac0c..d672f3c0ab 100644
--- a/tests/src/Interop/IJW/NativeCallingManaged/IjwNativeCallingManagedDll/IjwNativeCallingManagedDll.cpp
+++ b/tests/src/Interop/IJW/IjwNativeCallingManagedDll/IjwNativeCallingManagedDll.cpp
@@ -1,12 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+#include "platformdefines.h"
#pragma managed
-int ManagedCallee()
-{
- return 100;
-}
+int ManagedCallee();
#pragma unmanaged
int NativeFunction()
@@ -14,12 +12,34 @@ int NativeFunction()
return ManagedCallee();
}
+extern "C" DLL_EXPORT int __cdecl NativeEntryPoint()
+{
+ return NativeFunction();
+}
+
#pragma managed
public ref class TestClass
{
+private:
+ static int s_valueToReturn = 100;
public:
int ManagedEntryPoint()
{
return NativeFunction();
}
+
+ static void ChangeReturnedValue(int i)
+ {
+ s_valueToReturn = i;
+ }
+
+ static int GetReturnValue()
+ {
+ return s_valueToReturn;
+ }
};
+
+int ManagedCallee()
+{
+ return TestClass::GetReturnValue();
+}
diff --git a/tests/src/Interop/IJW/ManagedCallingNative/IjwNativeDll/CMakeLists.txt b/tests/src/Interop/IJW/IjwNativeDll/CMakeLists.txt
index 612e6aa17f..612e6aa17f 100644
--- a/tests/src/Interop/IJW/ManagedCallingNative/IjwNativeDll/CMakeLists.txt
+++ b/tests/src/Interop/IJW/IjwNativeDll/CMakeLists.txt
diff --git a/tests/src/Interop/IJW/ManagedCallingNative/IjwNativeDll/IjwNativeDll.cpp b/tests/src/Interop/IJW/IjwNativeDll/IjwNativeDll.cpp
index cb25b445a4..cb25b445a4 100644
--- a/tests/src/Interop/IJW/ManagedCallingNative/IjwNativeDll/IjwNativeDll.cpp
+++ b/tests/src/Interop/IJW/IjwNativeDll/IjwNativeDll.cpp
diff --git a/tests/src/Interop/IJW/LoadIjwFromModuleHandle/LoadIjwFromModuleHandle.cs b/tests/src/Interop/IJW/LoadIjwFromModuleHandle/LoadIjwFromModuleHandle.cs
new file mode 100644
index 0000000000..e44d534158
--- /dev/null
+++ b/tests/src/Interop/IJW/LoadIjwFromModuleHandle/LoadIjwFromModuleHandle.cs
@@ -0,0 +1,88 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using Internal.Runtime.InteropServices;
+using TestLibrary;
+
+using Console = Internal.Console;
+
+namespace LoadIjwFromModuleHandle
+{
+ class LoadIjwFromModuleHandle
+ {
+ unsafe static int Main(string[] args)
+ {
+ // Disable running on Windows 7 until IJW activation work is complete.
+ if(Environment.OSVersion.Platform != PlatformID.Win32NT || (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor == 1))
+ {
+ return 100;
+ }
+
+ try
+ {
+ HostPolicyMock.Initialize(Environment.CurrentDirectory, null);
+
+ // Load our fake mscoree to prevent desktop from loading.
+ NativeLibrary.Load(Path.Combine(Environment.CurrentDirectory, "mscoree.dll"));
+
+ Console.WriteLine("Verify that we can load an IJW assembly from native code.");
+ string ijwModulePath = Path.Combine(Environment.CurrentDirectory, "IjwNativeCallingManagedDll.dll");
+ IntPtr ijwNativeHandle = NativeLibrary.Load(ijwModulePath);
+
+ using (HostPolicyMock.Mock_corehost_resolve_component_dependencies(
+ 0,
+ ijwModulePath,
+ string.Empty,
+ string.Empty))
+ fixed (char* path = ijwModulePath)
+ {
+ InMemoryAssemblyLoader.LoadInMemoryAssembly(ijwNativeHandle, (IntPtr)path);
+ }
+
+ NativeEntryPointDelegate nativeEntryPoint = Marshal.GetDelegateForFunctionPointer<NativeEntryPointDelegate>(NativeLibrary.GetExport(ijwNativeHandle, "NativeEntryPoint"));
+
+ Assert.AreEqual(100, nativeEntryPoint());
+
+ Console.WriteLine("Test calls from managed to native to managed when an IJW assembly was first loaded via native.");
+
+ Assembly ijwAssemblyManaged = Assembly.Load("IjwNativeCallingManagedDll");
+ Type testType = ijwAssemblyManaged.GetType("TestClass");
+ object testInstance = Activator.CreateInstance(testType);
+ MethodInfo testMethod = testType.GetMethod("ManagedEntryPoint");
+
+ Assert.AreEqual(100, (int)testMethod.Invoke(testInstance, null));
+
+ MethodInfo changeReturnedValueMethod = testType.GetMethod("ChangeReturnedValue");
+ MethodInfo getReturnValueMethod = testType.GetMethod("GetReturnValue");
+
+ int newValue = 42;
+ changeReturnedValueMethod.Invoke(null, new object[] { newValue });
+
+ Assert.AreEqual(newValue, (int)getReturnValueMethod.Invoke(null, null));
+
+ // Native images are only loaded into memory once. As a result, the stubs in the vtfixup table
+ // will always point to JIT stubs that exist in the first ALC that the module was loaded into.
+ // As a result, if an IJW module is loaded into two different ALCs, or if the module is
+ // first loaded via a native call and then loaded via the managed loader, the call stack can change ALCs when
+ // jumping from managed->native->managed code within the IJW module.
+ Assert.AreEqual(100, (int)testMethod.Invoke(testInstance, null));
+ return 100;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(ex.ToString());
+
+ return 101;
+ }
+ }
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ delegate int NativeEntryPointDelegate();
+
+ }
+}
diff --git a/tests/src/Interop/IJW/LoadIjwFromModuleHandle/LoadIjwFromModuleHandle.csproj b/tests/src/Interop/IJW/LoadIjwFromModuleHandle/LoadIjwFromModuleHandle.csproj
new file mode 100644
index 0000000000..6228f2c8d7
--- /dev/null
+++ b/tests/src/Interop/IJW/LoadIjwFromModuleHandle/LoadIjwFromModuleHandle.csproj
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <AssemblyName>LoadIjwFromModuleHandle</AssemblyName>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{8B76A001-5654-4F11-A80B-EF12644EAD3D}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+ <ReferenceSystemPrivateCoreLib>true</ReferenceSystemPrivateCoreLib>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+
+ <!-- IJW is Windows-only -->
+ <!-- Test unsupported outside of windows -->
+ <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows>
+ <DisableProjectBuild Condition="'$(TargetsUnix)' == 'true'">true</DisableProjectBuild>
+
+ <!-- IJW is not supported on ARM64 -->
+ <DisableProjectBuild Condition="'$(Platform)' == 'arm64'">true</DisableProjectBuild>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+ </PropertyGroup>
+ <PropertyGroup>
+ <CopyDebugCRTDllsToOutputDirectory>true</CopyDebugCRTDllsToOutputDirectory>
+ </PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="LoadIjwFromModuleHandle.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="../IjwNativeCallingManagedDll/CMakeLists.txt" />
+ <ProjectReference Include="../ijwhostmock/CMakeLists.txt" />
+ <ProjectReference Include="../../../Common/hostpolicymock/CMakeLists.txt" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Interop.settings.targets))\Interop.settings.targets" />
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/tests/src/Interop/IJW/ManagedCallingNative/ManagedCallingNative.csproj b/tests/src/Interop/IJW/ManagedCallingNative/ManagedCallingNative.csproj
index 55b632155c..c8c1ccae96 100644
--- a/tests/src/Interop/IJW/ManagedCallingNative/ManagedCallingNative.csproj
+++ b/tests/src/Interop/IJW/ManagedCallingNative/ManagedCallingNative.csproj
@@ -37,8 +37,8 @@
<Compile Include="ManagedCallingNative.cs" />
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="IjwNativeDll/CMakeLists.txt" />
- <ProjectReference Include="../FakeMscoree/CMakeLists.txt" />
+ <ProjectReference Include="../IjwNativeDll/CMakeLists.txt" />
+ <ProjectReference Include="../ijwhostmock/CMakeLists.txt" />
<ProjectReference Include="../../../Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
diff --git a/tests/src/Interop/IJW/NativeCallingManaged/NativeCallingManaged.csproj b/tests/src/Interop/IJW/NativeCallingManaged/NativeCallingManaged.csproj
index 9decb48bf8..d40837d7c6 100644
--- a/tests/src/Interop/IJW/NativeCallingManaged/NativeCallingManaged.csproj
+++ b/tests/src/Interop/IJW/NativeCallingManaged/NativeCallingManaged.csproj
@@ -37,8 +37,8 @@
<Compile Include="NativeCallingManaged.cs" />
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="IjwNativeCallingManagedDll/CMakeLists.txt" />
- <ProjectReference Include="../FakeMscoree/CMakeLists.txt" />
+ <ProjectReference Include="../IjwNativeCallingManagedDll/CMakeLists.txt" />
+ <ProjectReference Include="../ijwhostmock/CMakeLists.txt" />
<ProjectReference Include="../../../Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
diff --git a/tests/src/Interop/IJW/FakeMscoree/CMakeLists.txt b/tests/src/Interop/IJW/ijwhostmock/CMakeLists.txt
index cfb6ee2ab5..bdcf920dc1 100644
--- a/tests/src/Interop/IJW/FakeMscoree/CMakeLists.txt
+++ b/tests/src/Interop/IJW/ijwhostmock/CMakeLists.txt
@@ -1,7 +1,7 @@
cmake_minimum_required (VERSION 2.6)
project (mscoree)
include_directories( ${INC_PLATFORM_DIR} )
-set(SOURCES mscoree.cpp mscoree.def)
+set(SOURCES mscoree.cpp)
# add the shared library
add_library (mscoree SHARED ${SOURCES})
diff --git a/tests/src/Interop/IJW/ijwhostmock/mscoree.cpp b/tests/src/Interop/IJW/ijwhostmock/mscoree.cpp
new file mode 100644
index 0000000000..7c398cb1a3
--- /dev/null
+++ b/tests/src/Interop/IJW/ijwhostmock/mscoree.cpp
@@ -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.
+// See the LICENSE file in the project root for more information.
+
+#include <windows.h>
+#include <xplatform.h>
+#include <set>
+
+std::set<HINSTANCE> g_modulesQueried = {};
+
+#if defined _X86_
+// We need to use a double-underscore here because the VC linker drops the first underscore
+// to help people who are exporting cdecl functions to easily export the right thing.
+#pragma comment(linker, "/export:__CorDllMain=__CorDllMain@12")
+#pragma comment(linker, "/export:GetTokenForVTableEntry=_GetTokenForVTableEntry@8")
+#endif
+
+// Entry-point that coreclr looks for.
+extern "C" DLL_EXPORT INT32 STDMETHODCALLTYPE GetTokenForVTableEntry(HINSTANCE hInst, BYTE **ppVTEntry)
+{
+ g_modulesQueried.emplace(hInst);
+ return (INT32)(UINT_PTR)*ppVTEntry;
+}
+
+extern "C" DLL_EXPORT BOOL __cdecl WasModuleVTableQueried(HINSTANCE hInst)
+{
+ return g_modulesQueried.find(hInst) != g_modulesQueried.end() ? TRUE : FALSE;
+}
+
+// Entrypoint jumped to by IJW dlls when their dllmain is called
+extern "C" DLL_EXPORT BOOL WINAPI _CorDllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved)
+{
+ return TRUE;
+}
diff --git a/tests/src/Interop/SizeConst/SizeConstTest.cs b/tests/src/Interop/SizeConst/SizeConstTest.cs
index a4d85c898e..b4b90552cb 100644
--- a/tests/src/Interop/SizeConst/SizeConstTest.cs
+++ b/tests/src/Interop/SizeConst/SizeConstTest.cs
@@ -26,7 +26,7 @@ class Test
{
// always marshal managedArray.Length
S_CHARArray_ByValTStr s = new S_CHARArray_ByValTStr();
- s.arr = "有个可爱";
+ s.arr = "abcd";
TakeByValTStr(s, s.arr.Length);
// off by one byte since sizeconst == 4 and
diff --git a/tests/src/JIT/Directed/VectorABI/VectorMgdMgd.cs b/tests/src/JIT/Directed/VectorABI/VectorMgdMgd.cs
index 99a77abd53..e241dc74da 100644
--- a/tests/src/JIT/Directed/VectorABI/VectorMgdMgd.cs
+++ b/tests/src/JIT/Directed/VectorABI/VectorMgdMgd.cs
@@ -12,10 +12,16 @@ using System.Runtime.InteropServices;
// as well as passing various numbers of them (so that we exceed the available registers),
// and mixing the HVA parameters with non-HVA parameters.
-// This is a skeleton version. Remaining to do:
-// - Add more HVA types: using Vector64 and with varying numbers of vectors.
+// This Test case covers all cases for
+// Methods that take one HVA argument with between 1 and 9 Vectror64 or Vector128 elements
+// - Called normally or by using reflection
+// Methods that return an HVA with between 1 and 9 Vectror64 or Vector128 elements
+// - Called normally or by using reflection
+
+// Remaining Test cases to do:
+// - Add tests that have one than one HVA argument
// - Add types that are *not* HVA types (e.g. too many vectors, or using Vector256).
-// - Add diagnostic info so that it is easier to see what has failed.
+// - Add tests that use a mix of HVA and non-HVA arguments
public static class VectorMgdMgd
{
@@ -24,9 +30,79 @@ public static class VectorMgdMgd
static Random random = new Random(12345);
+ public static T GetValueFromInt<T>(int value)
+ {
+ if (typeof(T) == typeof(float))
+ {
+ float floatValue = (float)value;
+ return (T)(object)floatValue;
+ }
+ if (typeof(T) == typeof(double))
+ {
+ double doubleValue = (double)value;
+ return (T)(object)doubleValue;
+ }
+ if (typeof(T) == typeof(int))
+ {
+ return (T)(object)value;
+ }
+ if (typeof(T) == typeof(uint))
+ {
+ uint uintValue = (uint)value;
+ return (T)(object)uintValue;
+ }
+ if (typeof(T) == typeof(long))
+ {
+ long longValue = (long)value;
+ return (T)(object)longValue;
+ }
+ if (typeof(T) == typeof(ulong))
+ {
+ ulong longValue = (ulong)value;
+ return (T)(object)longValue;
+ }
+ if (typeof(T) == typeof(ushort))
+ {
+ return (T)(object)(ushort)value;
+ }
+ if (typeof(T) == typeof(byte))
+ {
+ return (T)(object)(byte)value;
+ }
+ if (typeof(T) == typeof(short))
+ {
+ return (T)(object)(short)value;
+ }
+ if (typeof(T) == typeof(sbyte))
+ {
+ return (T)(object)(sbyte)value;
+ }
+ else
+ {
+ throw new ArgumentException();
+ }
+ }
+ public static bool CheckValue<T>(T value, T expectedValue)
+ {
+ bool returnVal;
+ if (typeof(T) == typeof(float))
+ {
+ returnVal = Math.Abs(((float)(object)value) - ((float)(object)expectedValue)) <= Single.Epsilon;
+ }
+ if (typeof(T) == typeof(double))
+ {
+ returnVal = Math.Abs(((double)(object)value) - ((double)(object)expectedValue)) <= Double.Epsilon;
+ }
+ else
+ {
+ returnVal = value.Equals(expectedValue);
+ }
+ return returnVal;
+ }
+
public unsafe class HVATests<T> where T : struct
{
- // We can have up to 4 vectors in an HVA, and we'll test structs with up to 5 of them
+ // An HVA can contain up to 4 vectors, so we'll test structs with up to 5 of them
// (to ensure that even those that are too large are handled consistently).
// So we need 5 * the element count in the largest (128-bit) vector with the smallest
// element type (byte).
@@ -34,6 +110,61 @@ public static class VectorMgdMgd
static T[] check;
private int ElementCount = (Unsafe.SizeOf<Vector128<T>>() / sizeof(byte)) * 5;
public bool isPassing = true;
+ public bool isReflection = false;
+
+ Type[] reflectionParameterTypes;
+ System.Reflection.MethodInfo reflectionMethodInfo;
+ object[] reflectionInvokeArgs;
+
+ ////////////////////////////////////////
+
+ public struct HVA64_01
+ {
+ public Vector64<T> v0;
+ }
+
+ public struct HVA64_02
+ {
+ public Vector64<T> v0;
+ public Vector64<T> v1;
+ }
+
+ public struct HVA64_03
+ {
+ public Vector64<T> v0;
+ public Vector64<T> v1;
+ public Vector64<T> v2;
+ }
+
+ public struct HVA64_04
+ {
+ public Vector64<T> v0;
+ public Vector64<T> v1;
+ public Vector64<T> v2;
+ public Vector64<T> v3;
+ }
+
+ public struct HVA64_05
+ {
+ public Vector64<T> v0;
+ public Vector64<T> v1;
+ public Vector64<T> v2;
+ public Vector64<T> v3;
+ public Vector64<T> v4;
+ }
+
+ private HVA64_01 hva64_01;
+ private HVA64_02 hva64_02;
+ private HVA64_03 hva64_03;
+ private HVA64_04 hva64_04;
+ private HVA64_05 hva64_05;
+
+ ////////////////////////////////////////
+
+ public struct HVA128_01
+ {
+ public Vector128<T> v0;
+ }
public struct HVA128_02
{
@@ -41,6 +172,115 @@ public static class VectorMgdMgd
public Vector128<T> v1;
}
+ public struct HVA128_03
+ {
+ public Vector128<T> v0;
+ public Vector128<T> v1;
+ public Vector128<T> v2;
+ }
+
+ public struct HVA128_04
+ {
+ public Vector128<T> v0;
+ public Vector128<T> v1;
+ public Vector128<T> v2;
+ public Vector128<T> v3;
+ }
+
+ public struct HVA128_05
+ {
+ public Vector128<T> v0;
+ public Vector128<T> v1;
+ public Vector128<T> v2;
+ public Vector128<T> v3;
+ public Vector128<T> v4;
+ }
+
+ private HVA128_01 hva128_01;
+ private HVA128_02 hva128_02;
+ private HVA128_03 hva128_03;
+ private HVA128_04 hva128_04;
+ private HVA128_05 hva128_05;
+
+ ////////////////////////////////////////
+
+ public void Init_HVAs()
+ {
+ int i;
+
+ i = 0;
+ hva64_01.v0 = Unsafe.As<T, Vector64<T>>(ref values[i]);
+
+ i = 0;
+ hva64_02.v0 = Unsafe.As<T, Vector64<T>>(ref values[i]);
+ i += Vector64<T>.Count;
+ hva64_02.v1 = Unsafe.As<T, Vector64<T>>(ref values[i]);
+
+ i = 0;
+ hva64_03.v0 = Unsafe.As<T, Vector64<T>>(ref values[i]);
+ i += Vector64<T>.Count;
+ hva64_03.v1 = Unsafe.As<T, Vector64<T>>(ref values[i]);
+ i += Vector64<T>.Count;
+ hva64_03.v2 = Unsafe.As<T, Vector64<T>>(ref values[i]);
+
+ i = 0;
+ hva64_04.v0 = Unsafe.As<T, Vector64<T>>(ref values[i]);
+ i += Vector64<T>.Count;
+ hva64_04.v1 = Unsafe.As<T, Vector64<T>>(ref values[i]);
+ i += Vector64<T>.Count;
+ hva64_04.v2 = Unsafe.As<T, Vector64<T>>(ref values[i]);
+ i += Vector64<T>.Count;
+ hva64_04.v3 = Unsafe.As<T, Vector64<T>>(ref values[i]);
+
+ i = 0;
+ hva64_05.v0 = Unsafe.As<T, Vector64<T>>(ref values[i]);
+ i += Vector64<T>.Count;
+ hva64_05.v1 = Unsafe.As<T, Vector64<T>>(ref values[i]);
+ i += Vector64<T>.Count;
+ hva64_05.v2 = Unsafe.As<T, Vector64<T>>(ref values[i]);
+ i += Vector64<T>.Count;
+ hva64_05.v3 = Unsafe.As<T, Vector64<T>>(ref values[i]);
+ i += Vector64<T>.Count;
+ hva64_05.v4 = Unsafe.As<T, Vector64<T>>(ref values[i]);
+
+ ////////////////////////////////////////
+
+ i = 0;
+ hva128_01.v0 = Unsafe.As<T, Vector128<T>>(ref values[i]);
+
+ i = 0;
+ hva128_02.v0 = Unsafe.As<T, Vector128<T>>(ref values[i]);
+ i += Vector128<T>.Count;
+ hva128_02.v1 = Unsafe.As<T, Vector128<T>>(ref values[i]);
+
+ i = 0;
+ hva128_03.v0 = Unsafe.As<T, Vector128<T>>(ref values[i]);
+ i += Vector128<T>.Count;
+ hva128_03.v1 = Unsafe.As<T, Vector128<T>>(ref values[i]);
+ i += Vector128<T>.Count;
+ hva128_03.v2 = Unsafe.As<T, Vector128<T>>(ref values[i]);
+
+ i = 0;
+ hva128_04.v0 = Unsafe.As<T, Vector128<T>>(ref values[i]);
+ i += Vector128<T>.Count;
+ hva128_04.v1 = Unsafe.As<T, Vector128<T>>(ref values[i]);
+ i += Vector128<T>.Count;
+ hva128_04.v2 = Unsafe.As<T, Vector128<T>>(ref values[i]);
+ i += Vector128<T>.Count;
+ hva128_04.v3 = Unsafe.As<T, Vector128<T>>(ref values[i]);
+
+ i = 0;
+ hva128_05.v0 = Unsafe.As<T, Vector128<T>>(ref values[i]);
+ i += Vector128<T>.Count;
+ hva128_05.v1 = Unsafe.As<T, Vector128<T>>(ref values[i]);
+ i += Vector128<T>.Count;
+ hva128_05.v2 = Unsafe.As<T, Vector128<T>>(ref values[i]);
+ i += Vector128<T>.Count;
+ hva128_05.v3 = Unsafe.As<T, Vector128<T>>(ref values[i]);
+ i += Vector128<T>.Count;
+ hva128_05.v4 = Unsafe.As<T, Vector128<T>>(ref values[i]);
+ }
+
public HVATests()
{
values = new T[ElementCount];
@@ -49,166 +289,801 @@ public static class VectorMgdMgd
int data = random.Next(100);
values[i] = GetValueFromInt<T>(data);
}
+
+ Init_HVAs();
}
- public void doTests()
+ // Checks that the values in v correspond to those in the values array starting
+ // with values[index]
+ private void checkValues(string msg, Vector64<T> v, int index)
{
- HVA128_02 hva128_02;
- hva128_02.v0 = Unsafe.As<T, Vector128<T>>(ref values[0]);
- hva128_02.v1 = Unsafe.As<T, Vector128<T>>(ref values[Vector128<T>.Count]);
- test1Argument_HVA128_02(hva128_02);
-
- testReturn_HFA128_02();
+ bool printedMsg = false; // Print at most one message
- Type[] parameterTypes = new Type[] { typeof(HVA128_02) };
- System.Reflection.MethodInfo methodInfo = typeof(HVATests<T>)
- .GetMethod(nameof(HVATests<T>.test1Argument_HVA128_02), parameterTypes);
- methodInfo.Invoke(this, new object[] { hva128_02 });
+ for (int i = 0; i < Vector64<T>.Count; i++)
+ {
+ if (!CheckValue<T>(v.GetElement(i), values[index]))
+ {
+ if (!printedMsg)
+ {
+ Console.WriteLine("{0}: FAILED - Vector64<T> checkValues(index = {1}, i = {2}) {3}",
+ msg, index, i, isReflection ? "(via reflection)" : "" );
+ printedMsg = true;
+ }
- testReflectionReturn_HFA128_02();
+ // Record failure status in global isPassing
+ isPassing = false;
+ }
+ index++;
+ }
}
// Checks that the values in v correspond to those in the values array starting
// with values[index]
- private void checkValues(Vector128<T> v, int index)
+ private void checkValues(string msg, Vector128<T> v, int index)
{
+ bool printedMsg = false; // Print at most one message
+
for (int i = 0; i < Vector128<T>.Count; i++)
{
if (!CheckValue<T>(v.GetElement(i), values[index]))
{
+ if (!printedMsg)
+ {
+ Console.WriteLine("{0}: FAILED - Vector64<T> checkValues(index = {1}, i = {2}) {3}",
+ msg, index, i, isReflection ? "(via reflection)" : "" );
+ printedMsg = true;
+ }
+
+ // Record failure status in global isPassing
isPassing = false;
}
index++;
}
}
+ public void Done_Reflection()
+ {
+ isReflection = false;
+ }
+
+ //========== Vector64<T> tests
+
+ //==================== Tests for passing 1 argumnet of HVA64_01
+
+ // Test the case where we've passed in a single argument HVA of 1 vector.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public void test1Argument_HVA64_01(HVA64_01 arg1)
+ {
+ checkValues("test1Argument_HVA64_01(arg1.vo)", arg1.v0, 0);
+ }
+
+ public void Init_Reflection_Args_HVA64_01()
+ {
+ isReflection = true;
+ reflectionParameterTypes = new Type[] { typeof(HVA64_01) };
+ reflectionMethodInfo = typeof(HVATests<T>).GetMethod(nameof(HVATests<T>.test1Argument_HVA64_01), reflectionParameterTypes);
+ reflectionInvokeArgs = new object[] { hva64_01 };
+ }
+
+ //==================== Tests for passing 1 argumnet of HVA64_02
+
// Test the case where we've passed in a single argument HVA of 2 vectors.
[MethodImpl(MethodImplOptions.NoInlining)]
- public void test1Argument_HVA128_02(HVA128_02 s)
+ public void test1Argument_HVA64_02(HVA64_02 arg1)
+ {
+ checkValues("test1Argument_HVA64_02(arg1.v0)", arg1.v0, 0);
+ checkValues("test1Argument_HVA64_02(arg1.v1)", arg1.v1, Vector64<T>.Count);
+ }
+
+ public void Init_Reflection_Args_HVA64_02()
+ {
+ isReflection = true;
+ reflectionParameterTypes = new Type[] { typeof(HVA64_02) };
+ reflectionMethodInfo = typeof(HVATests<T>).GetMethod(nameof(HVATests<T>.test1Argument_HVA64_02), reflectionParameterTypes);
+ reflectionInvokeArgs = new object[] { hva64_02 };
+ }
+
+ //==================== Tests for passing 1 argumnet of HVA64_03
+
+ // Test the case where we've passed in a single argument HVA of 3 vectors.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public void test1Argument_HVA64_03(HVA64_03 arg1)
+ {
+ checkValues("test1Argument_HVA64_03(arg1.v0)", arg1.v0, 0);
+ checkValues("test1Argument_HVA64_03(arg1.v1)", arg1.v1, 1 * Vector64<T>.Count);
+ checkValues("test1Argument_HVA64_03(arg1.v2)", arg1.v2, 2 * Vector64<T>.Count);
+ }
+
+ public void Init_Reflection_Args_HVA64_03()
+ {
+ isReflection = true;
+ reflectionParameterTypes = new Type[] { typeof(HVA64_03) };
+ reflectionMethodInfo = typeof(HVATests<T>).GetMethod(nameof(HVATests<T>.test1Argument_HVA64_03), reflectionParameterTypes);
+ reflectionInvokeArgs = new object[] { hva64_03 };
+ }
+
+ //==================== Tests for passing 1 argumnet of HVA64_04
+
+ // Test the case where we've passed in a single argument HVA of 4 vectors.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public void test1Argument_HVA64_04(HVA64_04 arg1)
+ {
+ checkValues("test1Argument_HVA64_04(arg1.v0)", arg1.v0, 0);
+ checkValues("test1Argument_HVA64_04(arg1.v1)", arg1.v1, 1 * Vector64<T>.Count);
+ checkValues("test1Argument_HVA64_04(arg1.v2)", arg1.v2, 2 * Vector64<T>.Count);
+ checkValues("test1Argument_HVA64_04(arg1.v3)", arg1.v3, 3 * Vector64<T>.Count);
+ }
+
+ public void Init_Reflection_Args_HVA64_04()
+ {
+ isReflection = true;
+ reflectionParameterTypes = new Type[] { typeof(HVA64_04) };
+ reflectionMethodInfo = typeof(HVATests<T>).GetMethod(nameof(HVATests<T>.test1Argument_HVA64_04), reflectionParameterTypes);
+ reflectionInvokeArgs = new object[] { hva64_04 };
+ }
+
+ //==================== Tests for passing 1 argumnet of HVA64_05
+
+ // Test the case where we've passed in a single argument HVA of 5 vectors.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public void test1Argument_HVA64_05(HVA64_05 arg1)
+ {
+ checkValues("test1Argument_HVA64_05(arg1.v0)", arg1.v0, 0);
+ checkValues("test1Argument_HVA64_05(arg1.v1)", arg1.v1, 1 * Vector64<T>.Count);
+ checkValues("test1Argument_HVA64_05(arg1.v2)", arg1.v2, 2 * Vector64<T>.Count);
+ checkValues("test1Argument_HVA64_05(arg1.v3)", arg1.v3, 3 * Vector64<T>.Count);
+ checkValues("test1Argument_HVA64_05(arg1.v4)", arg1.v4, 4 * Vector64<T>.Count);
+ }
+
+ public void Init_Reflection_Args_HVA64_05()
+ {
+ isReflection = true;
+ reflectionParameterTypes = new Type[] { typeof(HVA64_05) };
+ reflectionMethodInfo = typeof(HVATests<T>).GetMethod(nameof(HVATests<T>.test1Argument_HVA64_05), reflectionParameterTypes);
+ reflectionInvokeArgs = new object[] { hva64_05 };
+ }
+
+ //=============== Tests for return values if HVA64
+
+ //==================== Tests for return values of HVA64_01
+
+ // Return an HVA of 1 vectors, with values from the 'values' array.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public HVA64_01 returnTest_HVA64_01()
{
- checkValues(s.v0, 0);
- checkValues(s.v1, Vector128<T>.Count);
+ return hva64_01;
}
+ public void testReturn_HVA64_01()
+ {
+ HVA64_01 result = returnTest_HVA64_01();
+ checkValues("testReturn_HVA64_01(result.v0)",result.v0, 0);
+ }
+
+ public void Init_Reflection_Return_HVA64_01()
+ {
+ isReflection = true;
+ reflectionParameterTypes = new Type[] { };
+ reflectionMethodInfo = typeof(HVATests<T>).GetMethod(nameof(HVATests<T>.returnTest_HVA64_01), reflectionParameterTypes);
+ reflectionInvokeArgs = new object[] { };
+ }
+
+ public void testReflectionReturn_HVA64_01()
+ {
+ Init_Reflection_Return_HVA64_01();
+
+ object objResult = reflectionMethodInfo.Invoke(this, reflectionInvokeArgs);
+
+ HVA64_01 result = (HVA64_01)objResult;
+
+ checkValues("testReflectionReturn_HVA64_01(result.v0)",result.v0, 0);
+
+ Done_Reflection();
+ }
+
+ //==================== Tests for return values of HVA64_02
+
// Return an HVA of 2 vectors, with values from the 'values' array.
[MethodImpl(MethodImplOptions.NoInlining)]
- public HVA128_02 returnTest()
+ public HVA64_02 returnTest_HVA64_02()
{
- HVA128_02 hva128_02;
- hva128_02.v0 = Unsafe.As<T, Vector128<T>>(ref values[0]);
- hva128_02.v1 = Unsafe.As<T, Vector128<T>>(ref values[Vector128<T>.Count]);
- return hva128_02;
+ return hva64_02;
}
- public void testReturn_HFA128_02()
+ public void testReturn_HVA64_02()
{
- HVA128_02 s = returnTest();
- checkValues(s.v0, 0);
- checkValues(s.v1, Vector128<T>.Count);
+ HVA64_02 result = returnTest_HVA64_02();
+ checkValues("testReturn_HVA64_02(result.v0)",result.v0, 0);
+ checkValues("testReturn_HVA64_02(result.v1)",result.v1, Vector64<T>.Count);
}
- public void testReflectionReturn_HFA128_02()
+ public void Init_Reflection_Return_HVA64_02()
{
- Type[] parameterTypes = new Type[] {};
- System.Reflection.MethodInfo methodInfo = typeof(HVATests<T>)
- .GetMethod(nameof(HVATests<T>.returnTest), parameterTypes);
- object o = methodInfo.Invoke(this, new object[] { });
- HVA128_02 s = (HVA128_02)o;
- checkValues(s.v0, 0);
- checkValues(s.v1, Vector128<T>.Count);
+ isReflection = true;
+ reflectionParameterTypes = new Type[] { };
+ reflectionMethodInfo = typeof(HVATests<T>).GetMethod(nameof(HVATests<T>.returnTest_HVA64_02), reflectionParameterTypes);
+ reflectionInvokeArgs = new object[] { };
}
- }
- public static int Main(string[] args)
- {
- var isPassing = true;
+ public void testReflectionReturn_HVA64_02()
+ {
+ Init_Reflection_Return_HVA64_02();
- HVATests<byte> byteTests = new HVATests<byte>();
- byteTests.doTests();
+ object objResult = reflectionMethodInfo.Invoke(this, reflectionInvokeArgs);
- return byteTests.isPassing ? PASS : FAIL;
- }
+ HVA64_02 result = (HVA64_02)objResult;
- public static T GetValueFromInt<T>(int value)
- {
- if (typeof(T) == typeof(float))
+ checkValues("testReflectionReturn_HVA64_02(result.v0)",result.v0, 0);
+ checkValues("testReflectionReturn_HVA64_02(result.v1)",result.v1, Vector64<T>.Count);
+
+ Done_Reflection();
+ }
+
+ //==================== Tests for return values of HVA64_03
+
+ // Return an HVA of 3 vectors, with values from the 'values' array.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public HVA64_03 returnTest_HVA64_03()
{
- float floatValue = (float)value;
- return (T)(object)floatValue;
+ return hva64_03;
}
- if (typeof(T) == typeof(double))
+
+ public void testReturn_HVA64_03()
{
- double doubleValue = (double)value;
- return (T)(object)doubleValue;
+ HVA64_03 result = returnTest_HVA64_03();
+ checkValues("testReturn_HVA64_03(result.v0)",result.v0, 0);
+ checkValues("testReturn_HVA64_03(result.v1)",result.v1, 1 * Vector64<T>.Count);
+ checkValues("testReturn_HVA64_03(result.v2)",result.v2, 2 * Vector64<T>.Count);
}
- if (typeof(T) == typeof(int))
+
+ public void Init_Reflection_Return_HVA64_03()
{
- return (T)(object)value;
+ isReflection = true;
+ reflectionParameterTypes = new Type[] { };
+ reflectionMethodInfo = typeof(HVATests<T>).GetMethod(nameof(HVATests<T>.returnTest_HVA64_03), reflectionParameterTypes);
+ reflectionInvokeArgs = new object[] { };
}
- if (typeof(T) == typeof(uint))
+
+ public void testReflectionReturn_HVA64_03()
{
- uint uintValue = (uint)value;
- return (T)(object)uintValue;
+ Init_Reflection_Return_HVA64_03();
+
+ object objResult = reflectionMethodInfo.Invoke(this, reflectionInvokeArgs);
+
+ HVA64_03 result = (HVA64_03)objResult;
+
+ checkValues("testReflectionReturn_HVA64_03(result.v0)",result.v0, 0);
+ checkValues("testReflectionReturn_HVA64_03(result.v1)",result.v1, 1 * Vector64<T>.Count);
+ checkValues("testReflectionReturn_HVA64_03(result.v2)",result.v2, 2 * Vector64<T>.Count);
+
+ Done_Reflection();
}
- if (typeof(T) == typeof(long))
+
+ //==================== Tests for return values of HVA64_04
+
+ // Return an HVA of 4 vectors, with values from the 'values' array.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public HVA64_04 returnTest_HVA64_04()
{
- long longValue = (long)value;
- return (T)(object)longValue;
+ return hva64_04;
}
- if (typeof(T) == typeof(ulong))
+
+ public void testReturn_HVA64_04()
{
- ulong longValue = (ulong)value;
- return (T)(object)longValue;
+ HVA64_04 result = returnTest_HVA64_04();
+ checkValues("testReturn_HVA64_04(result.v0)",result.v0, 0);
+ checkValues("testReturn_HVA64_04(result.v1)",result.v1, 1 * Vector64<T>.Count);
+ checkValues("testReturn_HVA64_04(result.v2)",result.v2, 2 * Vector64<T>.Count);
+ checkValues("testReturn_HVA64_04(result.v3)",result.v3, 3 * Vector64<T>.Count);
}
- if (typeof(T) == typeof(ushort))
+
+ public void Init_Reflection_Return_HVA64_04()
{
- return (T)(object)(ushort)value;
+ isReflection = true;
+ reflectionParameterTypes = new Type[] { };
+ reflectionMethodInfo = typeof(HVATests<T>).GetMethod(nameof(HVATests<T>.returnTest_HVA64_04), reflectionParameterTypes);
+ reflectionInvokeArgs = new object[] { };
}
- if (typeof(T) == typeof(byte))
+
+ public void testReflectionReturn_HVA64_04()
{
- return (T)(object)(byte)value;
+ Init_Reflection_Return_HVA64_04();
+
+ object objResult = reflectionMethodInfo.Invoke(this, reflectionInvokeArgs);
+
+ HVA64_04 result = (HVA64_04)objResult;
+
+ checkValues("testReflectionReturn_HVA64_04(result.v0)",result.v0, 0);
+ checkValues("testReflectionReturn_HVA64_04(result.v1)",result.v1, 1 * Vector64<T>.Count);
+ checkValues("testReflectionReturn_HVA64_04(result.v2)",result.v2, 2 * Vector64<T>.Count);
+ checkValues("testReflectionReturn_HVA64_04(result.v3)",result.v3, 3 * Vector64<T>.Count);
+
+ Done_Reflection();
}
- if (typeof(T) == typeof(short))
+
+ //==================== Tests for return values of HVA64_05
+
+ // Return an HVA of 5 vectors, with values from the 'values' array.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public HVA64_05 returnTest_HVA64_05()
{
- return (T)(object)(short)value;
+ return hva64_05;
}
- if (typeof(T) == typeof(sbyte))
+
+ public void testReturn_HVA64_05()
{
- return (T)(object)(sbyte)value;
+ HVA64_05 result = returnTest_HVA64_05();
+ checkValues("testReturn_HVA64_05(result.v0)",result.v0, 0);
+ checkValues("testReturn_HVA64_05(result.v1)",result.v1, 1 * Vector64<T>.Count);
+ checkValues("testReturn_HVA64_05(result.v2)",result.v2, 2 * Vector64<T>.Count);
+ checkValues("testReturn_HVA64_05(result.v3)",result.v3, 3 * Vector64<T>.Count);
+ checkValues("testReturn_HVA64_05(result.v4)",result.v4, 4 * Vector64<T>.Count);
}
- else
+
+ public void Init_Reflection_Return_HVA64_05()
{
- throw new ArgumentException();
+ isReflection = true;
+ reflectionParameterTypes = new Type[] { };
+ reflectionMethodInfo = typeof(HVATests<T>).GetMethod(nameof(HVATests<T>.returnTest_HVA64_05), reflectionParameterTypes);
+ reflectionInvokeArgs = new object[] { };
}
- }
- public static bool CheckValue<T>(T value, T expectedValue)
- {
- bool returnVal;
- if (typeof(T) == typeof(float))
+
+ public void testReflectionReturn_HVA64_05()
{
- returnVal = Math.Abs(((float)(object)value) - ((float)(object)expectedValue)) <= Single.Epsilon;
+ Init_Reflection_Return_HVA64_05();
+
+ object objResult = reflectionMethodInfo.Invoke(this, reflectionInvokeArgs);
+
+ HVA64_05 result = (HVA64_05)objResult;
+
+ checkValues("testReflectionReturn_HVA64_05(result.v0)",result.v0, 0);
+ checkValues("testReflectionReturn_HVA64_05(result.v1)",result.v1, 1 * Vector64<T>.Count);
+ checkValues("testReflectionReturn_HVA64_05(result.v2)",result.v2, 2 * Vector64<T>.Count);
+ checkValues("testReflectionReturn_HVA64_05(result.v3)",result.v3, 3 * Vector64<T>.Count);
+ checkValues("testReflectionReturn_HVA64_05(result.v4)",result.v4, 4 * Vector64<T>.Count);
+
+ Done_Reflection();
}
- if (typeof(T) == typeof(double))
+
+ //========== Vector128<T> tests
+
+ //==================== Tests for passing 1 argumnet of HVA128_01
+
+ // Test the case where we've passed in a single argument HVA of 1 vectors.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public void test1Argument_HVA128_01(HVA128_01 arg1)
{
- returnVal = Math.Abs(((double)(object)value) - ((double)(object)expectedValue)) <= Double.Epsilon;
+ checkValues("test1Argument_HVA128_01(arg1.v0)", arg1.v0, 0);
}
- else
+
+ public void Init_Reflection_Args_HVA128_01()
{
- returnVal = value.Equals(expectedValue);
+ isReflection = true;
+ reflectionParameterTypes = new Type[] { typeof(HVA128_01) };
+ reflectionMethodInfo = typeof(HVATests<T>).GetMethod(nameof(HVATests<T>.test1Argument_HVA128_01), reflectionParameterTypes);
+ reflectionInvokeArgs = new object[] { hva128_01 };
}
- if (returnVal == false)
+
+ //==================== Tests for passing 1 argumnet of HVA128_02
+
+ // Test the case where we've passed in a single argument HVA of 2 vectors.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public void test1Argument_HVA128_02(HVA128_02 arg1)
{
- if ((typeof(T) == typeof(double)) || (typeof(T) == typeof(float)))
- {
- Console.WriteLine("CheckValue failed for type " + typeof(T).ToString() + ". Expected: {0} , Got: {1}", expectedValue, value);
- }
- else
- {
- Console.WriteLine("CheckValue failed for type " + typeof(T).ToString() + ". Expected: {0} (0x{0:X}), Got: {1} (0x{1:X})", expectedValue, value);
- }
+ checkValues("test1Argument_HVA128_02(arg1.v0)", arg1.v0, 0);
+ checkValues("test1Argument_HVA128_02(arg1.v1)", arg1.v1, Vector128<T>.Count);
+ }
+
+ public void Init_Reflection_Args_HVA128_02()
+ {
+ isReflection = true;
+ reflectionParameterTypes = new Type[] { typeof(HVA128_02) };
+ reflectionMethodInfo = typeof(HVATests<T>).GetMethod(nameof(HVATests<T>.test1Argument_HVA128_02), reflectionParameterTypes);
+ reflectionInvokeArgs = new object[] { hva128_02 };
+ }
+
+ //==================== Tests for passing 1 argumnet of HVA128_03
+
+ // Test the case where we've passed in a single argument HVA of 2 vectors.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public void test1Argument_HVA128_03(HVA128_03 arg1)
+ {
+ checkValues("test1Argument_HVA128_03(arg1.v0)", arg1.v0, 0);
+ checkValues("test1Argument_HVA128_03(arg1.v1)", arg1.v1, 1 * Vector128<T>.Count);
+ checkValues("test1Argument_HVA128_03(arg1.v2)", arg1.v2, 2 * Vector128<T>.Count);
+ }
+
+ public void Init_Reflection_Args_HVA128_03()
+ {
+ isReflection = true;
+ reflectionParameterTypes = new Type[] { typeof(HVA128_03) };
+ reflectionMethodInfo = typeof(HVATests<T>).GetMethod(nameof(HVATests<T>.test1Argument_HVA128_03), reflectionParameterTypes);
+ reflectionInvokeArgs = new object[] { hva128_03 };
+ }
+
+ //==================== Tests for passing 1 argumnet of HVA128_04
+
+ // Test the case where we've passed in a single argument HVA of 2 vectors.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public void test1Argument_HVA128_04(HVA128_04 arg1)
+ {
+ checkValues("test1Argument_HVA128_04(arg1.v0)", arg1.v0, 0);
+ checkValues("test1Argument_HVA128_04(arg1.v1)", arg1.v1, 1 * Vector128<T>.Count);
+ checkValues("test1Argument_HVA128_04(arg1.v2)", arg1.v2, 2 * Vector128<T>.Count);
+ checkValues("test1Argument_HVA128_04(arg1.v3)", arg1.v3, 3 * Vector128<T>.Count);
+ }
+
+ public void Init_Reflection_Args_HVA128_04()
+ {
+ isReflection = true;
+ reflectionParameterTypes = new Type[] { typeof(HVA128_04) };
+ reflectionMethodInfo = typeof(HVATests<T>).GetMethod(nameof(HVATests<T>.test1Argument_HVA128_04), reflectionParameterTypes);
+ reflectionInvokeArgs = new object[] { hva128_04 };
+ }
+
+ //==================== Tests for passing 1 argumnet of HVA128_05
+
+ // Test the case where we've passed in a single argument HVA of 2 vectors.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public void test1Argument_HVA128_05(HVA128_05 arg1)
+ {
+ checkValues("test1Argument_HVA128_05(arg1.v0)", arg1.v0, 0);
+ checkValues("test1Argument_HVA128_05(arg1.v1)", arg1.v1, 1 * Vector128<T>.Count);
+ checkValues("test1Argument_HVA128_05(arg1.v2)", arg1.v2, 2 * Vector128<T>.Count);
+ checkValues("test1Argument_HVA128_05(arg1.v3)", arg1.v3, 3 * Vector128<T>.Count);
+ checkValues("test1Argument_HVA128_05(arg1.v4)", arg1.v4, 4 * Vector128<T>.Count);
+ }
+
+ public void Init_Reflection_Args_HVA128_05()
+ {
+ isReflection = true;
+ reflectionParameterTypes = new Type[] { typeof(HVA128_05) };
+ reflectionMethodInfo = typeof(HVATests<T>).GetMethod(nameof(HVATests<T>.test1Argument_HVA128_05), reflectionParameterTypes);
+ reflectionInvokeArgs = new object[] { hva128_05 };
+ }
+
+ //==================== Tests for return values of HVA128_01
+
+ // Return an HVA of 1 vector, with values from the 'values' array.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public HVA128_01 returnTest_HVA128_01()
+ {
+ return hva128_01;
+ }
+
+ public void testReturn_HVA128_01()
+ {
+ HVA128_01 result = returnTest_HVA128_01();
+ checkValues("testReturn_HVA128_01(result.v0)",result.v0, 0);
+ }
+
+ public void Init_Reflection_Return_HVA128_01()
+ {
+ reflectionParameterTypes = new Type[] { };
+ reflectionMethodInfo = typeof(HVATests<T>).GetMethod(nameof(HVATests<T>.returnTest_HVA128_01), reflectionParameterTypes);
+ reflectionInvokeArgs = new object[] { };
+ }
+
+ public void testReflectionReturn_HVA128_01()
+ {
+ Init_Reflection_Return_HVA128_01();
+
+ object objResult = reflectionMethodInfo.Invoke(this, reflectionInvokeArgs);
+
+ HVA128_01 result = (HVA128_01)objResult;
+
+ checkValues("testReflectionReturn_HVA128_01(result.v0)",result.v0, 0);
+
+ Done_Reflection();
+ }
+
+ //==================== Tests for return values of HVA128_02
+
+ // Return an HVA of 2 vectors, with values from the 'values' array.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public HVA128_02 returnTest_HVA128_02()
+ {
+ return hva128_02;
+ }
+
+ public void testReturn_HVA128_02()
+ {
+ HVA128_02 result = returnTest_HVA128_02();
+ checkValues("testReturn_HVA128_02(result.v0)",result.v0, 0);
+ checkValues("testReturn_HVA128_02(result.v1)",result.v1, Vector128<T>.Count);
+ }
+
+ public void Init_Reflection_Return_HVA128_02()
+ {
+ isReflection = true;
+ reflectionParameterTypes = new Type[] { };
+ reflectionMethodInfo = typeof(HVATests<T>).GetMethod(nameof(HVATests<T>.returnTest_HVA128_02), reflectionParameterTypes);
+ reflectionInvokeArgs = new object[] { };
+ }
+
+ public void testReflectionReturn_HVA128_02()
+ {
+ Init_Reflection_Return_HVA128_02();
+
+ object objResult = reflectionMethodInfo.Invoke(this, reflectionInvokeArgs);
+
+ HVA128_02 result = (HVA128_02)objResult;
+
+ checkValues("testReflectionReturn_HVA128_02(result.v0)",result.v0, 0);
+ checkValues("testReflectionReturn_HVA128_02(result.v1)",result.v1, Vector128<T>.Count);
+
+ Done_Reflection();
+ }
+
+ //==================== Tests for return values of HVA128_03
+
+ // Return an HVA of 3 vectors, with values from the 'values' array.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public HVA128_03 returnTest_HVA128_03()
+ {
+ return hva128_03;
+ }
+
+ public void testReturn_HVA128_03()
+ {
+ HVA128_03 result = returnTest_HVA128_03();
+ checkValues("testReturn_HVA128_03(result.v0)",result.v0, 0);
+ checkValues("testReturn_HVA128_03(result.v1)",result.v1, 1 * Vector128<T>.Count);
+ checkValues("testReturn_HVA128_03(result.v2)",result.v2, 2 * Vector128<T>.Count);
+ }
+
+ public void Init_Reflection_Return_HVA128_03()
+ {
+ isReflection = true;
+ reflectionParameterTypes = new Type[] { };
+ reflectionMethodInfo = typeof(HVATests<T>).GetMethod(nameof(HVATests<T>.returnTest_HVA128_03), reflectionParameterTypes);
+ reflectionInvokeArgs = new object[] { };
+ }
+
+ public void testReflectionReturn_HVA128_03()
+ {
+ Init_Reflection_Return_HVA128_03();
+
+ object objResult = reflectionMethodInfo.Invoke(this, reflectionInvokeArgs);
+
+ HVA128_03 result = (HVA128_03)objResult;
+
+ checkValues("testReflectionReturn_HVA128_03(result.v0)",result.v0, 0);
+ checkValues("testReflectionReturn_HVA128_03(result.v1)",result.v1, 1 * Vector128<T>.Count);
+ checkValues("testReflectionReturn_HVA128_03(result.v2)",result.v2, 2 * Vector128<T>.Count);
+
+ Done_Reflection();
+ }
+
+ //==================== Tests for return values of HVA128_04
+
+ // Return an HVA of 3 vectors, with values from the 'values' array.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public HVA128_04 returnTest_HVA128_04()
+ {
+ return hva128_04;
+ }
+
+ public void testReturn_HVA128_04()
+ {
+ HVA128_04 result = returnTest_HVA128_04();
+ checkValues("testReturn_HVA128_04(result.v0)",result.v0, 0);
+ checkValues("testReturn_HVA128_04(result.v1)",result.v1, 1 * Vector128<T>.Count);
+ checkValues("testReturn_HVA128_04(result.v2)",result.v2, 2 * Vector128<T>.Count);
+ checkValues("testReturn_HVA128_04(result.v3)",result.v3, 3 * Vector128<T>.Count);
+ }
+
+ public void Init_Reflection_Return_HVA128_04()
+ {
+ isReflection = true;
+ reflectionParameterTypes = new Type[] { };
+ reflectionMethodInfo = typeof(HVATests<T>).GetMethod(nameof(HVATests<T>.returnTest_HVA128_04), reflectionParameterTypes);
+ reflectionInvokeArgs = new object[] { };
+ }
+
+ public void testReflectionReturn_HVA128_04()
+ {
+ Init_Reflection_Return_HVA128_04();
+
+ object objResult = reflectionMethodInfo.Invoke(this, reflectionInvokeArgs);
+
+ HVA128_04 result = (HVA128_04)objResult;
+
+ checkValues("testReflectionReturn_HVA128_04(result.v0)",result.v0, 0);
+ checkValues("testReflectionReturn_HVA128_04(result.v1)",result.v1, 1 * Vector128<T>.Count);
+ checkValues("testReflectionReturn_HVA128_04(result.v2)",result.v2, 2 * Vector128<T>.Count);
+ checkValues("testReflectionReturn_HVA128_04(result.v3)",result.v3, 3 * Vector128<T>.Count);
+
+ Done_Reflection();
+ }
+
+ //==================== Tests for return values of HVA128_05
+
+ // Return an HVA of 3 vectors, with values from the 'values' array.
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public HVA128_05 returnTest_HVA128_05()
+ {
+ return hva128_05;
+ }
+
+ public void testReturn_HVA128_05()
+ {
+ HVA128_05 result = returnTest_HVA128_05();
+ checkValues("testReturn_HVA128_05(result.v0)",result.v0, 0);
+ checkValues("testReturn_HVA128_05(result.v1)",result.v1, 1 * Vector128<T>.Count);
+ checkValues("testReturn_HVA128_05(result.v2)",result.v2, 2 * Vector128<T>.Count);
+ checkValues("testReturn_HVA128_05(result.v3)",result.v3, 3 * Vector128<T>.Count);
+ checkValues("testReturn_HVA128_05(result.v4)",result.v4, 4 * Vector128<T>.Count);
+ }
+
+ public void Init_Reflection_Return_HVA128_05()
+ {
+ isReflection = true;
+ reflectionParameterTypes = new Type[] { };
+ reflectionMethodInfo = typeof(HVATests<T>).GetMethod(nameof(HVATests<T>.returnTest_HVA128_05), reflectionParameterTypes);
+ reflectionInvokeArgs = new object[] { };
+ }
+
+ public void testReflectionReturn_HVA128_05()
+ {
+ Init_Reflection_Return_HVA128_05();
+
+ object objResult = reflectionMethodInfo.Invoke(this, reflectionInvokeArgs);
+
+ HVA128_05 result = (HVA128_05)objResult;
+
+ checkValues("testReflectionReturn_HVA128_05(result.v0)",result.v0, 0);
+ checkValues("testReflectionReturn_HVA128_05(result.v1)",result.v1, 1 * Vector128<T>.Count);
+ checkValues("testReflectionReturn_HVA128_05(result.v2)",result.v2, 2 * Vector128<T>.Count);
+ checkValues("testReflectionReturn_HVA128_05(result.v3)",result.v3, 3 * Vector128<T>.Count);
+ checkValues("testReflectionReturn_HVA128_05(result.v4)",result.v4, 4 * Vector128<T>.Count);
+
+ Done_Reflection();
+ }
+
+ //////////////////////////////////////////////////
+
+ public void doTests()
+ {
+ ////// Vector64<T> tests
+
+ // Test HVA Vector64<T> Arguments
+
+ test1Argument_HVA64_01(hva64_01);
+
+ Init_Reflection_Args_HVA64_01();
+ reflectionMethodInfo.Invoke(this, reflectionInvokeArgs);
+ Done_Reflection();
+
+
+ test1Argument_HVA64_02(hva64_02);
+
+ Init_Reflection_Args_HVA64_02();
+ reflectionMethodInfo.Invoke(this, reflectionInvokeArgs);
+ Done_Reflection();
+
+
+ test1Argument_HVA64_03(hva64_03);
+
+ Init_Reflection_Args_HVA64_03();
+ reflectionMethodInfo.Invoke(this, reflectionInvokeArgs);
+ Done_Reflection();
+
+
+ test1Argument_HVA64_04(hva64_04);
+
+ Init_Reflection_Args_HVA64_04();
+ reflectionMethodInfo.Invoke(this, reflectionInvokeArgs);
+ Done_Reflection();
+
+
+ test1Argument_HVA64_05(hva64_05);
+
+ Init_Reflection_Args_HVA64_05();
+ reflectionMethodInfo.Invoke(this, reflectionInvokeArgs);
+ Done_Reflection();
+
+
+ // Test HVA Vector64<T> Return values
+
+ testReturn_HVA64_01();
+
+ testReflectionReturn_HVA64_01();
+
+
+ testReturn_HVA64_02();
+
+ testReflectionReturn_HVA64_02();
+
+
+ testReturn_HVA64_03();
+
+ testReflectionReturn_HVA64_03();
+
+
+ testReturn_HVA64_04();
+
+ testReflectionReturn_HVA64_04();
+
+
+ testReturn_HVA64_05();
+
+ testReflectionReturn_HVA64_05();
+
+
+ ////// Vector128<T> tests
+
+ // Test HVA Vector128<T> Arguments
+
+ test1Argument_HVA128_01(hva128_01);
+
+ Init_Reflection_Args_HVA128_01();
+ reflectionMethodInfo.Invoke(this, reflectionInvokeArgs);
+ Done_Reflection();
+
+
+ test1Argument_HVA128_02(hva128_02);
+
+ Init_Reflection_Args_HVA128_02();
+ reflectionMethodInfo.Invoke(this, reflectionInvokeArgs);
+ Done_Reflection();
+
+
+ test1Argument_HVA128_03(hva128_03);
+
+ Init_Reflection_Args_HVA128_03();
+ reflectionMethodInfo.Invoke(this, reflectionInvokeArgs);
+ Done_Reflection();
+
+
+ test1Argument_HVA128_04(hva128_04);
+
+ Init_Reflection_Args_HVA128_04();
+ reflectionMethodInfo.Invoke(this, reflectionInvokeArgs);
+ Done_Reflection();
+
+
+ test1Argument_HVA128_05(hva128_05);
+
+ Init_Reflection_Args_HVA128_05();
+ reflectionMethodInfo.Invoke(this, reflectionInvokeArgs);
+ Done_Reflection();
+
+
+ // Test HVA Vector128<T> Return values
+
+ testReturn_HVA128_01();
+ testReflectionReturn_HVA128_01();
+
+ testReturn_HVA128_02();
+ testReflectionReturn_HVA128_02();
+
+ testReturn_HVA128_03();
+ testReflectionReturn_HVA128_03();
+
+ testReturn_HVA128_04();
+ testReflectionReturn_HVA128_04();
+
+ testReturn_HVA128_05();
+ testReflectionReturn_HVA128_05();
}
- return returnVal;
}
+ public static int Main(string[] args)
+ {
+
+ HVATests<byte> byteTests = new HVATests<byte>();
+ byteTests.doTests();
+ if (byteTests.isPassing)
+ {
+ Console.WriteLine("Test Passed");
+ }
+ else
+ {
+ Console.WriteLine("Test FAILED");
+ }
+
+ return byteTests.isPassing ? PASS : FAIL;
+ }
}
diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_18582/GitHub_18582.cs b/tests/src/JIT/Regression/JitBlue/GitHub_18582/GitHub_18582.cs
new file mode 100644
index 0000000000..0a589d18e8
--- /dev/null
+++ b/tests/src/JIT/Regression/JitBlue/GitHub_18582/GitHub_18582.cs
@@ -0,0 +1,221 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Threading;
+
+// Repro for issue fixed 18582 (also seein in 23309) -- stack overflow when remorphing
+// call with a lot of arguments and some CSEs when running with limited stack (as is
+// done when CLR is hosted by IIS).
+
+class GitHub_18582
+{
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static void Consume(
+ int x000, int x001, int x002, int x003, int x004, int x005, int x006, int x007, int x008, int x009,
+ int x010, int x011, int x012, int x013, int x014, int x015, int x016, int x017, int x018, int x019,
+ int x020, int x021, int x022, int x023, int x024, int x025, int x026, int x027, int x028, int x029,
+ int x030, int x031, int x032, int x033, int x034, int x035, int x036, int x037, int x038, int x039,
+ int x040, int x041, int x042, int x043, int x044, int x045, int x046, int x047, int x048, int x049,
+ int x050, int x051, int x052, int x053, int x054, int x055, int x056, int x057, int x058, int x059,
+ int x060, int x061, int x062, int x063, int x064, int x065, int x066, int x067, int x068, int x069,
+ int x070, int x071, int x072, int x073, int x074, int x075, int x076, int x077, int x078, int x079,
+ int x080, int x081, int x082, int x083, int x084, int x085, int x086, int x087, int x088, int x089,
+ int x090, int x091, int x092, int x093, int x094, int x095, int x096, int x097, int x098, int x099,
+ int x100, int x101, int x102, int x103, int x104, int x105, int x106, int x107, int x108, int x109,
+ int x110, int x111, int x112, int x113, int x114, int x115, int x116, int x117, int x118, int x119,
+ int x120, int x121, int x122, int x123, int x124, int x125, int x126, int x127, int x128, int x129,
+ int x130, int x131, int x132, int x133, int x134, int x135, int x136, int x137, int x138, int x139,
+ int x140, int x141, int x142, int x143, int x144, int x145, int x146, int x147, int x148, int x149,
+ int x150, int x151, int x152, int x153, int x154, int x155, int x156, int x157, int x158, int x159,
+ int x160, int x161, int x162, int x163, int x164, int x165, int x166, int x167, int x168, int x169,
+ int x170, int x171, int x172, int x173, int x174, int x175, int x176, int x177, int x178, int x179,
+ int x180, int x181, int x182, int x183, int x184, int x185, int x186, int x187, int x188, int x189,
+ int x190, int x191, int x192, int x193, int x194, int x195, int x196, int x197, int x198, int x199,
+ int x200, int x201, int x202, int x203, int x204, int x205, int x206, int x207, int x208, int x209,
+ int x210, int x211, int x212, int x213, int x214, int x215, int x216, int x217, int x218, int x219,
+ int x220, int x221, int x222, int x223, int x224, int x225, int x226, int x227, int x228, int x229,
+ int x230, int x231, int x232, int x233, int x234, int x235, int x236, int x237, int x238, int x239,
+ int x240, int x241, int x242, int x243, int x244, int x245, int x246, int x247, int x248, int x249,
+ int x250, int x251, int x252, int x253, int x254, int x255, int x256, int x257, int x258, int x259,
+ int x260, int x261, int x262, int x263, int x264, int x265, int x266, int x267, int x268, int x269,
+ int x270, int x271, int x272, int x273, int x274, int x275, int x276, int x277, int x278, int x279,
+ int x280, int x281, int x282, int x283, int x284, int x285, int x286, int x287, int x288, int x289,
+ int x290, int x291, int x292, int x293, int x294, int x295, int x296, int x297, int x298, int x299,
+ int x300, int x301, int x302, int x303, int x304, int x305, int x306, int x307, int x308, int x309,
+ int x310, int x311, int x312, int x313, int x314, int x315, int x316, int x317, int x318, int x319,
+ int x320, int x321, int x322, int x323, int x324, int x325, int x326, int x327, int x328, int x329,
+ int x330, int x331, int x332, int x333, int x334, int x335, int x336, int x337, int x338, int x339,
+ int x340, int x341, int x342, int x343, int x344, int x345, int x346, int x347, int x348, int x349,
+ int x350, int x351, int x352, int x353, int x354, int x355, int x356, int x357, int x358, int x359,
+ int x360, int x361, int x362, int x363, int x364, int x365, int x366, int x367, int x368, int x369,
+ int x370, int x371, int x372, int x373, int x374, int x375, int x376, int x377, int x378, int x379,
+ int x380, int x381, int x382, int x383, int x384, int x385, int x386, int x387, int x388, int x389,
+ int x390, int x391, int x392, int x393, int x394, int x395, int x396, int x397, int x398, int x399,
+ int x400, int x401, int x402, int x403, int x404, int x405, int x406, int x407, int x408, int x409,
+ int x410, int x411, int x412, int x413, int x414, int x415, int x416, int x417, int x418, int x419,
+ int x420, int x421, int x422, int x423, int x424, int x425, int x426, int x427, int x428, int x429,
+ int x430, int x431, int x432, int x433, int x434, int x435, int x436, int x437, int x438, int x439,
+ int x440, int x441, int x442, int x443, int x444, int x445, int x446, int x447, int x448, int x449,
+ int x450, int x451, int x452, int x453, int x454, int x455, int x456, int x457, int x458, int x459,
+ int x460, int x461, int x462, int x463, int x464, int x465, int x466, int x467, int x468, int x469,
+ int x470, int x471, int x472, int x473, int x474, int x475, int x476, int x477, int x478, int x479,
+ int x480, int x481, int x482, int x483, int x484, int x485, int x486, int x487, int x488, int x489,
+ int x490, int x491, int x492, int x493, int x494, int x495, int x496, int x497, int x498, int x499,
+ int x500, int x501, int x502, int x503, int x504, int x505, int x506, int x507, int x508, int x509,
+ int x510, int x511, int x512, int x513, int x514, int x515, int x516, int x517, int x518, int x519,
+ int x520, int x521, int x522, int x523, int x524, int x525, int x526, int x527, int x528, int x529,
+ int x530, int x531, int x532, int x533, int x534, int x535, int x536, int x537, int x538, int x539,
+ int x540, int x541, int x542, int x543, int x544, int x545, int x546, int x547, int x548, int x549,
+ int x550, int x551, int x552, int x553, int x554, int x555, int x556, int x557, int x558, int x559,
+ int x560, int x561, int x562, int x563, int x564, int x565, int x566, int x567, int x568, int x569,
+ int x570, int x571, int x572, int x573, int x574, int x575, int x576, int x577, int x578, int x579,
+ int x580, int x581, int x582, int x583, int x584, int x585, int x586, int x587, int x588, int x589,
+ int x590, int x591, int x592, int x593, int x594, int x595, int x596, int x597, int x598, int x599,
+ int x600, int x601, int x602, int x603, int x604, int x605, int x606, int x607, int x608, int x609,
+ int x610, int x611, int x612, int x613, int x614, int x615, int x616, int x617, int x618, int x619,
+ int x620, int x621, int x622, int x623, int x624, int x625, int x626, int x627, int x628, int x629,
+ int x630, int x631, int x632, int x633, int x634, int x635, int x636, int x637, int x638, int x639,
+ int x640, int x641, int x642, int x643, int x644, int x645, int x646, int x647, int x648, int x649,
+ int x650, int x651, int x652, int x653, int x654, int x655, int x656, int x657, int x658, int x659,
+ int x660, int x661, int x662, int x663, int x664, int x665, int x666, int x667, int x668, int x669,
+ int x670, int x671, int x672, int x673, int x674, int x675, int x676, int x677, int x678, int x679,
+ int x680, int x681, int x682, int x683, int x684, int x685, int x686, int x687, int x688, int x689,
+ int x690, int x691, int x692, int x693, int x694, int x695, int x696, int x697, int x698, int x699,
+ int x700, int x701, int x702, int x703, int x704, int x705, int x706, int x707, int x708, int x709,
+ int x710, int x711, int x712, int x713, int x714, int x715, int x716, int x717, int x718, int x719,
+ int x720, int x721, int x722, int x723, int x724, int x725, int x726, int x727, int x728, int x729,
+ int x730, int x731, int x732, int x733, int x734, int x735, int x736, int x737, int x738, int x739,
+ int x740, int x741, int x742, int x743, int x744, int x745, int x746, int x747, int x748, int x749,
+ int x750, int x751, int x752, int x753, int x754, int x755, int x756, int x757, int x758, int x759,
+ int x760, int x761, int x762, int x763, int x764, int x765, int x766, int x767, int x768, int x769,
+ int x770, int x771, int x772, int x773, int x774, int x775, int x776, int x777, int x778, int x779,
+ int x780, int x781, int x782, int x783, int x784, int x785, int x786, int x787, int x788, int x789,
+ int x790, int x791, int x792, int x793, int x794, int x795, int x796, int x797, int x798, int x799,
+ int x800, int x801, int x802, int x803, int x804, int x805, int x806, int x807, int x808, int x809,
+ int x810, int x811, int x812, int x813, int x814, int x815, int x816, int x817, int x818, int x819,
+ int x820, int x821, int x822, int x823, int x824, int x825, int x826, int x827, int x828, int x829,
+ int x830, int x831, int x832, int x833, int x834, int x835, int x836, int x837, int x838, int x839,
+ int x840, int x841, int x842, int x843, int x844, int x845, int x846, int x847, int x848, int x849)
+ {
+ s_x = x000 + x099 + x749 + x849;
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static int q() => s_x + 1;
+
+ public static void Test()
+ {
+ int z = s_x;
+ Consume(
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ q(), q(), q(), q(), q(), q(), q(), q(), q(), q(),
+ z + 1, z + 1, z + 1, z + 1, z + 1, z + 1, z + 1, z + 1, z + 1, z + 1);
+ }
+
+ static int s_x;
+
+ public static int Main()
+ {
+ s_x = 1;
+ int expected = 8;
+ Thread t = new Thread(Test, 512 * 1024);
+ t.Start();
+ t.Join();
+
+ if (s_x == expected)
+ {
+ Console.WriteLine("PASSED");
+ return 100;
+ }
+ else
+ {
+ Console.WriteLine($"FAILED, got {s_x}, expected {expected}");
+ return -1;
+ }
+ }
+}
diff --git a/tests/src/Regressions/coreclr/22728/debug15.ilproj b/tests/src/JIT/Regression/JitBlue/GitHub_18582/GitHub_18582.csproj
index 442a59cc4e..5243a9a46c 100644
--- a/tests/src/Regressions/coreclr/22728/debug15.ilproj
+++ b/tests/src/JIT/Regression/JitBlue/GitHub_18582/GitHub_18582.csproj
@@ -2,34 +2,38 @@
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<PropertyGroup>
- <AssemblyName>$(MSBuildProjectName)</AssemblyName>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <AssemblyName>$(MSBuildProjectName)</AssemblyName>
<SchemaVersion>2.0</SchemaVersion>
- <ProjectGuid>{85DFC527-4DB1-595E-A7D7-E94EE1F8140D}</ProjectGuid>
+ <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages</ReferencePath>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
<NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp>
- <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
- <ReferenceLocalMscorlib>true</ReferenceLocalMscorlib>
- <OutputType>Exe</OutputType>
- <CLRTestKind>BuildAndRun</CLRTestKind>
- <CLRTestPriority>0</CLRTestPriority>
+ <CLRTestPriority>1</CLRTestPriority>
</PropertyGroup>
-
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "></PropertyGroup>
<ItemGroup>
<CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
<Visible>False</Visible>
</CodeAnalysisDependentAssemblyPaths>
</ItemGroup>
-
+ <PropertyGroup>
+ <DebugType></DebugType>
+ <Optimize>True</Optimize>
+ </PropertyGroup>
<ItemGroup>
- <Compile Include="debug15.il" />
+ <Compile Include="GitHub_18582.cs" />
</ItemGroup>
-
-
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
-</Project>
+ <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project> \ No newline at end of file
diff --git a/tests/src/Regressions/coreclr/22728/createdelegate.il b/tests/src/Regressions/coreclr/22728/createdelegate.il
deleted file mode 100644
index 2aa0a614c2..0000000000
--- a/tests/src/Regressions/coreclr/22728/createdelegate.il
+++ /dev/null
@@ -1,103 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-.assembly extern System.Runtime { }
-.assembly extern System.Console { }
-.assembly createdelegate { }
-
-.class private auto ansi beforefieldinit A
- extends [System.Runtime]System.Object
- implements B,
- C,
- D
-{
- .method private hidebysig newslot virtual final
- instance char B.F1(int32 pA) cil managed
- {
- .override B::F1
- ldc.i4.s 65
- ret
- }
-
- .method private hidebysig newslot virtual final
- instance char D.F1(int32 pA) cil managed
- {
- .override D::F1
- ldc.i4.s 65
- ret
- }
-
- .method private hidebysig instance int32
- Test() cil managed
- {
- ldtoken class [System.Runtime]System.Func`2<int32,char>
- call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle)
- ldarg.0
- ldtoken method instance char D::F1(int32)
- call class [System.Runtime]System.Reflection.MethodBase [System.Runtime]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [System.Runtime]System.RuntimeMethodHandle)
- castclass class [System.Runtime]System.Reflection.MethodInfo
- call class [System.Runtime]System.Delegate [System.Runtime]System.Delegate::CreateDelegate(class [System.Runtime]System.Type, object, class [System.Runtime]System.Reflection.MethodInfo)
- castclass class [System.Runtime]System.Func`2<int32,char>
-
- ldc.i4.1
- callvirt instance !1 class [System.Runtime]System.Func`2<int32,char>::Invoke(!0)
- dup
- call void [System.Console]System.Console::WriteLine(char)
- ldc.i4.s 65
- ceq
- brtrue A_F1_OK
-
- ldc.i4.3
- ret
-
-A_F1_OK:
-
- ldc.i4.s 100
-
- ret
- }
-
- .method private hidebysig static int32 Main() cil managed
- {
- .entrypoint
- newobj instance void A::.ctor()
- call instance int32 A::Test()
- ret
- }
-
- .method public hidebysig specialname rtspecialname
- instance void .ctor() cil managed
- {
- ldarg.0
- call instance void [System.Runtime]System.Object::.ctor()
- ret
- }
-}
-
-.class interface private abstract auto ansi B
- implements C,
- D
-{
- .method public hidebysig newslot virtual
- instance char F1(int32 pB) cil managed
- {
- ldc.i4.s 66
- ret
- }
-}
-
-.class interface private abstract auto ansi C
- implements D
-{
-}
-
-.class interface private abstract auto ansi D
-{
- .method public hidebysig newslot virtual
- instance char F1(int32 pD) cil managed
- {
- ldc.i4.s 68
- ret
- }
-}
diff --git a/tests/src/Regressions/coreclr/22728/debug15.il b/tests/src/Regressions/coreclr/22728/debug15.il
deleted file mode 100644
index a545c9b2cf..0000000000
--- a/tests/src/Regressions/coreclr/22728/debug15.il
+++ /dev/null
@@ -1,133 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-.assembly extern System.Runtime { }
-.assembly extern System.Console { }
-.assembly debug15 { }
-
-.class private auto ansi beforefieldinit A
- extends [System.Runtime]System.Object
- implements B,
- C,
- D
-{
- .method private hidebysig newslot virtual final
- instance char B.F1(int32 pA) cil managed
- {
- .override B::F1
- ldc.i4.s 65
- ret
- }
-
- .method private hidebysig newslot virtual final
- instance char D.F1(int32 pA) cil managed
- {
- .override D::F1
- ldc.i4.s 65
- ret
- }
-
- .method private hidebysig instance int32
- Test() cil managed
- {
- ldarg.0
- ldftn instance char B::F1(int32)
- newobj instance void class [System.Runtime]System.Func`2<int32,char>::.ctor(object,
- native int)
- ldc.i4.1
- callvirt instance !1 class [System.Runtime]System.Func`2<int32,char>::Invoke(!0)
- dup
- call void [System.Console]System.Console::WriteLine(char)
- ldc.i4.s 66
- ceq
- brtrue B_F1_OK
-
- ldc.i4.1
- ret
-
-B_F1_OK:
-
- ldarg.0
- ldftn instance char D::F1(int32)
- newobj instance void class [System.Runtime]System.Func`2<int32,char>::.ctor(object,
- native int)
- ldc.i4.1
- callvirt instance !1 class [System.Runtime]System.Func`2<int32,char>::Invoke(!0)
- dup
- call void [System.Console]System.Console::WriteLine(char)
- ldc.i4.s 68
- ceq
- brtrue D_F1_OK
-
- ldc.i4.2
- ret
-
-D_F1_OK:
-
- ldarg.0
- dup
- ldvirtftn instance char D::F1(int32)
- newobj instance void class [System.Runtime]System.Func`2<int32,char>::.ctor(object,
- native int)
- ldc.i4.1
- callvirt instance !1 class [System.Runtime]System.Func`2<int32,char>::Invoke(!0)
- dup
- call void [System.Console]System.Console::WriteLine(char)
- ldc.i4.s 65
- ceq
- brtrue A_F1_OK
-
- ldc.i4.3
- ret
-
-A_F1_OK:
-
- ldc.i4.s 100
-
- ret
- }
-
- .method private hidebysig static int32 Main() cil managed
- {
- .entrypoint
- newobj instance void A::.ctor()
- call instance int32 A::Test()
- ret
- }
-
- .method public hidebysig specialname rtspecialname
- instance void .ctor() cil managed
- {
- ldarg.0
- call instance void [System.Runtime]System.Object::.ctor()
- ret
- }
-}
-
-.class interface private abstract auto ansi B
- implements C,
- D
-{
- .method public hidebysig newslot virtual
- instance char F1(int32 pB) cil managed
- {
- ldc.i4.s 66
- ret
- }
-}
-
-.class interface private abstract auto ansi C
- implements D
-{
-}
-
-.class interface private abstract auto ansi D
-{
- .method public hidebysig newslot virtual
- instance char F1(int32 pD) cil managed
- {
- ldc.i4.s 68
- ret
- }
-}
diff --git a/tests/src/tracing/eventcounter/pollingcounter.cs b/tests/src/tracing/eventcounter/pollingcounter.cs
new file mode 100644
index 0000000000..ba2369d639
--- /dev/null
+++ b/tests/src/tracing/eventcounter/pollingcounter.cs
@@ -0,0 +1,192 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#if USE_MDT_EVENTSOURCE
+using Microsoft.Diagnostics.Tracing;
+#else
+using System.Diagnostics.Tracing;
+#endif
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Reflection;
+
+namespace BasicEventSourceTests
+{
+ public partial class TestEventCounter
+ {
+ [EventSource(Name = "SimpleEventSource")]
+ private sealed class SimpleEventSource : EventSource
+ {
+ private object _failureCounter;
+ private object _successCounter;
+
+ public SimpleEventSource(Func<float> getFailureCount, Func<float> getSuccessCount, Type PollingCounterType)
+ {
+ _failureCounter = Activator.CreateInstance(PollingCounterType, "failureCount", this, getSuccessCount);
+ _successCounter = Activator.CreateInstance(PollingCounterType, "successCount", this, getFailureCount);
+ }
+ }
+
+ internal sealed class SimpleEventListener : EventListener
+ {
+ private readonly string _targetSourceName;
+ private readonly EventLevel _level;
+ private Dictionary<string, string> args;
+
+ public int FailureEventCount { get; private set; } = 0;
+ public int SuccessEventCount { get; private set; } = 0;
+ public bool Failed = false;
+
+ public SimpleEventListener(string targetSourceName, EventLevel level)
+ {
+ // Store the arguments
+ _targetSourceName = targetSourceName;
+ _level = level;
+ args = new Dictionary<string, string>();
+ args.Add("EventCounterIntervalSec", "1");
+ }
+
+ protected override void OnEventSourceCreated(EventSource source)
+ {
+ if (source.Name.Equals(_targetSourceName))
+ {
+ EnableEvents(source, _level, (EventKeywords)(-1), args);
+ }
+ }
+
+ protected override void OnEventWritten(EventWrittenEventArgs eventData)
+ {
+ if (eventData.EventName.Equals("EventCounters"))
+ {
+ for (int i = 0; i < eventData.Payload.Count; i++)
+ {
+
+ // Decode the payload
+ IDictionary<string, object> eventPayload = eventData.Payload[i] as IDictionary<string, object>;
+
+ string name = "";
+ string min = "";
+ string max = "";
+ string mean = "";
+ string stdev = "";
+
+ foreach (KeyValuePair<string, object> payload in eventPayload)
+ {
+ if (payload.Key.Equals("Name"))
+ {
+ name = payload.Value.ToString();
+ if (name.Equals("failureCount"))
+ FailureEventCount++;
+ else if (name.Equals("successCount"))
+ SuccessEventCount++;
+ }
+
+ else if (payload.Key.Equals("Min"))
+ {
+ min = payload.Value.ToString();
+ }
+ else if (payload.Key.Equals("Max"))
+ {
+ max = payload.Value.ToString();
+ }
+ else if (payload.Key.Equals("Mean"))
+ {
+ mean = payload.Value.ToString();
+ }
+ else if (payload.Key.Equals("StandardDeviation"))
+ {
+ stdev = payload.Value.ToString();
+ }
+ }
+
+ // Check if the mean is what we expect it to be
+ if (name.Equals("failureCount"))
+ {
+ if (Int32.Parse(mean) != successCountCalled)
+ {
+ Console.WriteLine($"Mean is not what we expected: {mean} vs {successCountCalled}");
+ Failed = true;
+ }
+ }
+ else if (name.Equals("successCount"))
+ {
+ if (Int32.Parse(mean) != failureCountCalled)
+ {
+ Console.WriteLine($"Mean is not what we expected: {mean} vs {failureCountCalled}");
+ }
+ }
+
+ // In PollingCounter, min/max/mean should have the same value since we aggregate value only once per counter
+ if (!min.Equals(mean) || !min.Equals(max))
+ {
+ Console.WriteLine("mean/min/max are not equal");
+ Failed = true;
+ }
+
+ // In PollingCounter, stdev should always be 0 since we aggregate value only once per counter.
+ if (!stdev.Equals("0"))
+ {
+ Console.WriteLine("standard deviation is not 0");
+ Failed = true;
+ }
+ }
+ }
+ }
+ }
+
+
+ public static int failureCountCalled = 0;
+ public static int successCountCalled = 0;
+
+ public static float getFailureCount()
+ {
+ failureCountCalled++;
+ return failureCountCalled;
+ }
+
+ public static float getSuccessCount()
+ {
+ successCountCalled++;
+ return successCountCalled;
+ }
+
+ public static int Main(string[] args)
+ {
+ // Create an EventListener.
+ using (SimpleEventListener myListener = new SimpleEventListener("SimpleEventSource", EventLevel.Verbose))
+ {
+ // Reflect over System.Private.CoreLib and get the PollingCounter type.
+ Assembly SPC = typeof(System.Diagnostics.Tracing.EventSource).Assembly;
+ if(SPC == null)
+ {
+ Console.WriteLine("Failed to get System.Private.CoreLib assembly.");
+ return 1;
+ }
+ Type PollingCounterType = SPC.GetType("System.Diagnostics.Tracing.PollingCounter");
+ if(PollingCounterType == null)
+ {
+ Console.WriteLine("Failed to get System.Diagnostics.Tracing.PollingCounter type.");
+ return 1;
+ }
+
+ SimpleEventSource eventSource = new SimpleEventSource(getFailureCount, getSuccessCount, PollingCounterType);
+
+ // Want to sleep for 5000 ms to get some counters piling up.
+ Thread.Sleep(5000);
+
+ if (myListener.FailureEventCount > 0 && myListener.SuccessEventCount > 0 && !myListener.Failed && (failureCountCalled > 0 && successCountCalled > 0))
+ {
+ Console.WriteLine("Test Passed");
+ return 100;
+ }
+ else
+ {
+ Console.WriteLine("Test Failed");
+ return 1;
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/src/Regressions/coreclr/22728/createdelegate.ilproj b/tests/src/tracing/eventcounter/pollingcounter.csproj
index d218608b54..a342c88199 100644
--- a/tests/src/Regressions/coreclr/22728/createdelegate.ilproj
+++ b/tests/src/tracing/eventcounter/pollingcounter.csproj
@@ -2,34 +2,29 @@
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<PropertyGroup>
- <AssemblyName>$(MSBuildProjectName)</AssemblyName>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<SchemaVersion>2.0</SchemaVersion>
- <ProjectGuid>{85DFC527-4DB1-595E-A7D7-E94EE1F8140D}</ProjectGuid>
- <FileAlignment>512</FileAlignment>
- <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
- <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp>
- <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
- <ReferenceLocalMscorlib>true</ReferenceLocalMscorlib>
+ <ProjectGuid>{8E3244CB-407F-4142-BAAB-E7A55901A5FA}</ProjectGuid>
<OutputType>Exe</OutputType>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
<CLRTestKind>BuildAndRun</CLRTestKind>
+ <DefineConstants>$(DefineConstants);STATIC</DefineConstants>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CLRTestPriority>0</CLRTestPriority>
+ <GCStressIncompatible>true</GCStressIncompatible>
+ <!-- This test has a secondary thread with an infinite loop -->
+ <UnloadabilityIncompatible>true</UnloadabilityIncompatible>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
</PropertyGroup>
-
- <ItemGroup>
- <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
- <Visible>False</Visible>
- </CodeAnalysisDependentAssemblyPaths>
- </ItemGroup>
-
- <ItemGroup>
- <Compile Include="createdelegate.il" />
- </ItemGroup>
-
-
<ItemGroup>
- <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ <Compile Include="pollingcounter.cs" />
+ <ProjectReference Include="../common/common.csproj" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project>